面试题五:Java泛型的实现机制是怎样的?
Java泛型的实现机制是怎样的?
一、面试官视角:这道题想考察什么?
- 对Java泛型是否仅停留在集合框架的使用
- 对Java泛型的实现机制的认知和理解
- 是否有足够的项目开发实战和“踩坑”经验
- 对泛型(或模版)编程是否有深入的对比研究
- 对常见的框架原理是否有过深入剖析
二、题目剖析:
1、类型擦除从编译角度的细节
2、类型擦除对运行时的影响
3、类型擦除对反射的影响
4、对比类型不擦除的语言(C#)
5、为什么Java选择类型擦除
类型擦除的好处:
1、运行时内存负担小(因为编译之后都是一个类型,如List<String>类型,编译之后就是List类型)
2、兼容性好(Jdk1.5才推出泛型,因此类型擦除是为了顾及Jdk1.0到1.4的版本)
类型擦除的问题:
1、基本类型无法作为泛型实参(就有了装箱和拆箱的开销问题)
2、泛型类型无法用作方法重载(因为类型擦除了,不管形参是List<String> 还是 List<Integer>类型,编译之后都是List类型)
3、泛型类型无法当做真实类型使用(因为类型擦除了,编译之后,所有T类型都会转换成Object类型,那么直接new 一个 Object类型的话,其实这不是我们想要的类型,而我们想要的是T类型的实际类型,但Java并不能new出来,因为Java根本不知道这个T类型到底是个啥)
4、静态方法无法引用类泛型参数(因为类泛型参数只有在类实例化之后才知道,而静态方法根本不需要有类的实例,但静态方法可以声明泛型参数)
1
2
3class GenericClass<T> {
public static <R> R max(R a, R b) {...}
}5、类型强转的运行时开销(因为类型擦除,在运行时,字节码会将类型进行强转,这样你才能使用具体类型的方法)
1
2
3
4
5
6
7
8
9
10
11在Jdk < 1.5:
List strings = new ArrayList();
strings.add("Hello");
String value = (String)strings.get(0); // 在代码中需要手动将类型进行强转,同时在字节码中也会将类型进行强转
在Jdk >= 1.5:
List<String> strings = new ArrayList<>();
strings.add("Hello");
String value = strings.get(0); // 在代码中我们使用了泛型,所以不需要手动将类型进行强转,但会在运行时的字节码中将类型进行强转,这是因为编译完成之后,类型被擦除了。
6、知识迁移:Gson.fromJson为什么需要传入Class?
1 |
|
还是那句话,在Java中,泛型在编译之后,都会被擦除,你不传具体的类型,我怎么给你转成你想要的类型。
7、附加的签名信息(Signatures)
1 |
|
1 |
|
8、知识迁移:使用泛型签名的两个实例
1 |
|
1 |
|
1 |
|
三、题目结论:
- Java泛型采用类型擦除实现(Java的实现机制就是类型擦除)
- 类型编译时被擦除为Object,所以不兼容基本类型
- 类型擦除的实现方案主要考虑后向兼容
- 泛型类型签名信息特定场景下反射可获取
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!