泛型
个人的一点理解,如有错误请指正,谢谢。
1. 什么是泛型?有什么作用?
泛型即参数化类型,用于解决数据类型的安全性问题,通过泛型参数可以指定传入的对象类型。比如创建集合的时候指定了集合的泛型为String类型,就表示该集合中只能存放String类型对象。
泛型注意事项:
- 泛型不能用基本数据类型
- 在给泛型指定具体类型后,可以传入该类型或者其子类类型
2. 泛型的使用方式有哪几种?
泛型类,泛型接口,泛型方法。即将泛型定义在类名后面,接口名后面,方法入参出参上。
3. 类型擦除
名词介绍
-
泛型类型:泛型类型是指具有类型参数的类或接口。
List<String>是一个泛型类型 -
类型参数:在
List<String>中,类型参数是String。 -
泛型的通配符:无界通配符
List<?>表示可以匹配任意类型;上界通配符List<? extends Number>表示一个匹配Number或其子类类型的列表;下界通配符List<? super Integer>表示一个匹配Integer或其父类类型的列表。
什么是类型擦除?
Java 泛型的实现原理是通过类型擦除来实现的。类型擦除是指在编译时将泛型类型的信息擦除,将泛型类型转换为它们的原始类型,并将类型参数替换为Object类型或限定类型。
类型擦除的过程主要有以下几个步骤:
- 替换类型参数:类型参数会被擦除为
Object类型或限定类型。例如,List<T>中的T会被擦除为Object,class MyClass<T extends Number>中的T会被替换为Number。 - 擦除泛型类型:泛型类型在编译时会被擦除为原始类型。例如,
List<String>在编译后会被擦除为List。 - 类型转换:在泛型代码中,编译器会插入必要的转换操作(类型强制转换、自动装箱/拆箱等)以保持类型安全性。
- 类型检查:编译器会在编译时进行类型检查以确保类型安全性。这样可以在编译时捕获类型错误。
泛型通过在编译时进行类型检查,来保证类型安全。编译器会捕获许多类型相关的错误,使得这些错误可以在编译阶段被发现和解决,而不是在运行时导致异常或错误的发生。
一旦程序经过编译并转换为字节码后,泛型的类型信息会被擦除,运行时无法获取泛型类型参数的具体信息。这就意味着在运行时无法直接操作泛型类型参数的具体类型。这也是为什么无法实例化泛型数组、直接获取泛型的具体类型或进行类型判断的原因。
需要注意的是,尽管类型擦除限制了在泛型代码内部操作具体类型参数的能力,但通过一些编程模式和技巧(如通配符、反射、工厂模式等),仍然可以实现泛型的灵活使用和类型安全性。
解释 “由于类型擦除的存在,泛型类型参数的具体类型在运行时是不可知的”,如下:
意味着在泛型代码内部,无法获取泛型类型参数的具体类型信息。举个例子,考虑以下代码:
public class MyClass<T> {
public void process(T obj) {
// 无法在运行时获取 T 的具体类型信息
}
}
MyClass<String> myClass = new MyClass<>();
myClass.process("Hello");
在上述代码中,泛型类 MyClass<T> 中的泛型类型参数 T 在编译时会被擦除为其上界或实际类型。因此,在 process() 方法内部,无法在运行时获取 T 的具体类型信息。无法直接知道 T 是否是 String 类型。
这意味着在泛型代码内部无法使用 T 的具体类型来执行特定的操作,如创建新的 T 对象、调用 T 特定的方法等。因为在运行时,泛型类型参数的具体类型信息已经被擦除。
通过对类型擦除的了解,我们在回过头来看使用泛型的一些限制条件就很清晰明朗了,如下
-
泛型类型参数不能是基本数据类型:因为当类型擦除后类型参数会变为
Object等引用类型,而基本数据类型不能被当做引用类型来处理。 -
使用
instanceof注意点:instanceof是用来判断一个对象是否为一个类的实例。由于类型擦除的存在,泛型类型参数的具体类型在运行时是不可知的,因此instanceof运算符不能直接用于泛型类型参数。 -
不能创建一个泛型类型的实例,也不能创建泛型数组:
T obj = new T(); // 不被允许的 T[] arr = new T[size]; // 不被允许应为其中的
T可能是Object或者其他限界,因此对其new没有意义。
2和3理论上解释是由于类型擦除,泛型的类型参数的具体类型在运行时是不可知的。
最终案例:
List<String> list = new ArrayList<>();
list.add(str);
在上述代码运行时,通过list.add(str)将元素 str 添加到 List<String> 中时,泛型类型参数的具体信息被擦除了。由于泛型类型参数在运行时不可知,编译器无法确切知道添加的元素的具体类型。
因此,添加的元素 a 会被当作 Object 类型处理,并且存储在 List 内部的对象数组中。从编译器的角度来看,list 是一个 List<String> 类型的对象,但在运行时(JVM角度),List<String> 会被擦除为原始类型 List。
当我们从 list 中取出元素时,编译器会自动进行类型转换。例如,通过 String element = list.get(0); 取出索引为 0 的元素时,编译器会将其强制转换为 String 类型,以符合泛型类型参数为 String 的声明。
文章介绍了Java中的泛型概念,它的作用在于提升数据类型安全性,通过泛型类、接口和方法实现参数化类型。类型擦除是Java泛型的实现机制,它在编译时擦除泛型信息,保证运行时兼容非泛型代码。由于类型擦除,泛型类型参数在运行时不可知,限制了某些操作,如无法直接创建泛型数组或使用instanceof检测。然而,编译器通过类型检查确保了类型安全。
1009

被折叠的 条评论
为什么被折叠?



