一篇吃透 Java 泛型:语法与底层原理
·
一、泛型的概念
泛型就是参数化类型,把数据类型当作参数传递,把类型当作参数传进去,用什么类型,就指定什么类型,代码更通用、更安全
泛型 = 万能盒子
- 你买一个盒子,不规定它装什么。
- 用的时候再说:装苹果、装书、装玩具。
- 泛型就是:先不写死类型,用的时候再指定
二、泛型的语法
1. 泛型类 / 接口
class 泛型类名称<类型形参列表> {
// 这里可以使用类型参数
}
class ClassName<T1, T2, ..., Tn> {
}
< T > 就是泛型参数,表示任意类型
T 可以是任意字母(E、K、V、T 都可以)
2. 泛型方法
public <T> T 方法名(T t) {
return t;
}
三、泛型类的使用
示例:
// 泛型类:T 代表传入的类型
class Box<T> {
private T data;
public void set(T data) {
this.data = data;
}
public T get() {
return data;
}
}
使用:
public class Test {
public static void main(String[] args) {
// 存字符串
Box<String> box1 = new Box<>();
box1.set("Hello");
String s = box1.get();
// 存数字
Box<Integer> box2 = new Box<>();
box2.set(100);
int i = box2.get();
}
}
注意
- 不能new泛型类型的数组,比如说:
T[] ts = new T[5];
- 泛型只能接受类,所有的基本数据类型必须使用包装类
四、泛型如何编译的(面试常考)
1. 擦除机制
通过命令:javap -c 查看Box.class 字节码文件,所有的T都是Object
在编译的过程当中,将所有的T替换为Object这种机制,我们称为:擦除机制
Java的泛型机制是在编译级别实现的。编译器生成的字节码在运行期间并不包含泛型的类型信息,也就是说编译完成后,生成 .class 文件时,会把 全部删掉
总结一下:
当你使用泛型时,编辑器做了三件事
- 编译期间:类型检查
- 比如你写了ArrayList < String >,编译器就只允许你 add String,放错类型直接报错!
- 编译后:擦除泛型
- < String > 直接删掉,变回原始类型(Object)
- 自动帮你强转
- 取数据时,编译器偷偷帮你加 (String)
五、泛型的上界
在定义泛型类时,有时需要对传入的类型变量做一定的约束,可以通过类型边界来约束,限制传入的类型,只能是 某个类 或 它的子类
语法:
class 泛型类名称<类型形参列表> extends 继承类/* 这里可以使用类型参数 */ {
// 这里可以使用类型参数
}
示例:
public static void show(Box<E extends Number> box) { }
上界 Number
- 能传:Integer、Double、Short……(都是 Number 的子类)
- 不能传:String、Student……(不是子类)
复杂示例:
public class MyArray<E extends Comparable<E>> {
...
}
E必须是实现了Comparable接口的


所有评论(0)