泛型
January 8, 2024About 2 min
泛型
Generics
JDK 5 中引入的一个新特性。使用泛型参数,可以增强代码的可读性以及稳定性。
编译器可以对泛型参数进行检测,并且通过泛型参数可以指定传入的对象类型。比如 ArrayList<Persion> persons = new ArrayList<Persion>()
这行代码就指明了该 ArrayList 对象只能传入 Persion 对象,如果传入其他类型的对象就会报错。
- 泛型类
- 泛型接口
- 泛型方法
泛型擦除
Java 的泛型是伪泛型,这是因为 Java 在编译期间,所有的泛型信息都会被擦掉,这也就是通常所说类型擦除 。
编译器会在编译期间会动态地将泛型 T 擦除为 Object 或将 T extends xxx 擦除为其限定类型 xxx 。
因此,泛型本质上其实还是编译器的行为,为了保证引入泛型机制但不创建新的类型,减少虚拟机的运行开销,编译器通过擦除将泛型类转化为一般类。
eg. 由于泛型擦除原理带来的一些特性:
- 泛型容器在编译期间添加不同类型会报错,但是运行期间通过反射可以添加
- 由于泛型擦除机制,泛型导致的重载方法会报错
既然编译器要把泛型擦除,那为什么还要用泛型呢?用 Object 代替不行吗?
这个问题其实在变相考察泛型的作用:
- 使用泛型可在编译期间进行类型检测。
- 使用 Object 类型需要手动添加强制类型转换,降低代码可读性,提高出错概率。
- 泛型可以使用自限定类型如
T extends Comparable
。
桥方法(Bridge Method)
用于继承泛型类时保证多态,桥方法为编译器自动生成。
限制
通配符
- extends
- super
- 假设你有一个
List<Integer>
,并且你尝试将其赋值给一个List<? extends Number>
类型的变量。从类型的角度来看,这是允许的,因为Integer
是Number
的子类型。 - 现在,如果
List<? extends Number>
允许添加任何Number
的子类型,例如Double
,那么你将能够将Double
添加到原本只能包含Integer
的列表中。这显然是类型不安全的。
因此,为了防止这种类型不匹配的情况发生,Java 在使用通配符上界时禁止往这样的列表中添加元素(除了 null
,因为 null
可以代表任何类型)。