<1>Java泛型是什么?
Java泛型方法和泛型类使程序员能够使用单个方法声明来分别指定一组相关方法,或者使用单个类声明来指定一组相关类型.
泛型还提供编译时类型的安全性,允许程序员在编译时捕获无效类型。
<2>Java泛型特点:
1.类型擦除
Java中的泛型基本上都是在编译器这个层次来实现的。在生成的Java字节代码中是不包含泛型中的类型信息的。使用泛型的时候加上的类型参数,会被编译器在编译的时候去掉。这个过程就称为类型擦除。
如在代码中定义的List<Object>和List<String>等类型,在编译之后都会变成List。JVM看到的只是List,而由泛型附加的类型信息对JVM来说是不可见的。Java编译器会在编译时尽可能的发现可能出错的地方,但是仍然无法避免在运行时刻出现类型转换异常的情况。2.通配符与上下界
通配符:
使用泛型类的时候,既可以指定一个具体的类型,如List<String>就声明了具体的类型是String;也可以用通配符?来表示未知类型
上下界:
因为对于List<?>中的元素只能用Object来引用,在有些情况下不是很方便。在这些情况下,可以使用上下界来限制未知类型的范围。- 上限通配符:如List<? extends Number>说明List中可能包含的元素类型是Number及其子类。这样就可以使用Number类的intValue等方法。
- 下限通配符: 如List<? super Number> 只需要接受Integer或其超类的实例Number类的实例
- 无界通配符:声明无限通配符参数,只需要列出问号(?)
List<Object>与List<?>区别:
List<Object>实际上确定了List中包含的是Object及其子类,在使用的时候都可以通过Object来进行引用。而List<?>则其中所包含的元素类型是不确定。其中可能包含的是String,也可能是 Integer。如果它包含了String的话,往里面添加Integer类型的元素就是错误的。正因为类型未知,就不能通过new ArrayList<?>()的方法来创建一个新的ArrayList对象。因为编译器无法知道具体的类型是什么。但是对于 List<?>中的元素确总是可以用Object来引用的,因为虽然类型未知,但肯定是Object及其子类。考虑下面的代码:
public void wildcard(List<?> list) {
list.add(1);//编译错误
}
如上所示,试图对一个带通配符的泛型类进行操作的时候,总是会出现编译错误。其原因在于通配符所表示的类型是未知的。
<3>泛型的限制:
很多泛型的奇怪特性都与这个类型擦除的存在有关,包括:• 使用泛型时,类型参数不允许为静态(static)
private static T t; // 编译错误 Cannot make a static reference to the non-static type T• 类型参数不能实例化其对象
private T t1 = new T(); //编译错误 Cannot instantiate the type T• 通用类不允许直接或间接扩展Throwable类
class Box<T> extends Throwable {} //编译错误 The generic class Box<T> may not subclass java.lang.Throwable• 不允许捕获一个类型参数的实例
因为异常处理是由JVM在运行时刻来进行的。由于类型信息被擦除,JVM是无法区分两个异常类型MyException<String>和MyException<Integer>的。对于JVM来说,它们都是 MyException类型的。也就无法执行与异常对应的catch语句。
public static <T extends Exception> void execute() {
try {
} catch (T e) { //编译异常 Cannot use the type parameter T in a catch block
}
}
<4>泛型-类型参数命名约定
常用的类型参数名称列表 -- E - 元素,主要由Java集合(Collections)框架使用。
- K - 键,主要用于表示映射中的键的参数类型。
- V - 值,主要用于表示映射中的值的参数类型。
- N - 数字,主要用于表示数字。
- T - 类型,主要用于表示第一类通用型参数。
- S - 类型,主要用于表示第二类通用类型参数。
- U - 类型,主要用于表示第三类通用类型参数。
- V - 类型,主要用于表示第四个通用类型参数
<5>类型擦除规则:
类型擦除指编译器使用实际的类或桥接方法替换泛型参数的过程. 在类型擦除中,编译器确保不会创建额外的类,并且没有运行时开销。
- 用通用类型的类型参数替换其绑定的有界类型参数。
- 如果使用无界类型参数,则使用Object替换类型参数。
- 插入类型转换以保护类型安全。
- 生成桥接方法以在扩展通用类型中保持多态。