什么是泛型擦除
# 怎么理解泛型擦除
Java 的泛型机制其实是伪泛型,因为他只作用于 Java 程序编译
期间(就是在你写代码的时候起作用);在运行期间并不存在。
编译器在编译期间会动态的将泛型 T
擦除为 Object ,或者将 T extends xxxClass
擦除为其限定类型 xxxClass
。
可以通过下述代码证明上面说法↓↓↓
@SuppressWarnings("all")
public class GenericTest1 {
public static void main(String[] args) {
Class<Demo> demoClass = Demo.class;
// 拿到 Demo 类的属性,并输出
Field[] declaredFields = demoClass.getDeclaredFields();
for (Field field : declaredFields) {
System.out.println(field.getName() + ":" + field.getType());
}
}
}
class Demo<T> {
T t;
Integer id;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
输出为:
t:class java.lang.Object
id:class java.lang.Integer
1
2
2
# 举例说明
在我们创建不同类型的 List 时候,会放入不同的泛型;在进行 add 的时候,可以限制 add 的类型,但是他们的类在最终比较的时候确实相同的;这就是泛型擦除造成的。
ArrayList<String> stringList = new ArrayList<>();
ArrayList<Integer> integerList = new ArrayList<>();
// 这里的输出 其实 true
System.out.println(stringList.getClass() == integerList.getClass());
// 这里的输出为 class java.util.ArrayList
System.out.println(stringList.getClass());
// 这里的输出为 class java.util.ArrayList
System.out.println(integerList.getClass());
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
但是 add 的类型限制也并不是绝对的,我们在在他运行时候(反射),进行动态的传参,可以做到往 integerList
中添加String类型的代码。
List<Integer> integerList = new ArrayList<>();
integerList.add(1);
// 通过反射来调用
Method method = integerList.getClass().getDeclaredMethod("add", Object.class);
method.invoke(integerList, "散装java");
// 这里的输出为 [1, 散装java]
System.out.println(integerList.toString());
1
2
3
4
5
6
7
2
3
4
5
6
7
以上的代码都能证明,泛型是在运行期间被擦除的!
另外以下写法会报错,也是因为泛型擦除的存在。报错为 两种方法具有相同的擦除
// 方法重载失败
// 错误提示为 'method(List<Integer>)' clashes with 'method(List<String>)'; both methods have same erasure
public void method(List<Integer> list){}
public void method(List<String> list){}
1
2
3
4
2
3
4
# 既然泛型擦除有问题,为什么还要使用呢,用Object代替不行?
使用泛型可以在编译期间进行类型检测,尽早发现问题
使用
Object
类型需要手动强制转换类型,而用泛型则可以节省了这个操作可以有效的避免classCastException
,代码可读性更高,出错率更低提升性能,编译完成后,基本就确定了类型,节省了强制类型转换带来的性能消耗
上次更新: 2022/11/27, 16:23:17