泛型
- 泛型
- 泛型数组与泛型容器
- 如何解决擦除导致的类型丢失
泛型
我们接着上面一篇文章继续聊泛型,如果没有看过的,欢迎阅读thinking in java - 泛型1
泛型数组与泛型容器
先看一段代码:
public class ArrayMaker<T> {
private Class<T> kind;
public ArrayMaker(Class<T> kind) {
this.kind = kind;
}
T[] creat(int size) {
return (T[]) Array.newInstance(kind, size);
}
public static void main(String[] args) {
ArrayMaker<String> arrayMaker = new ArrayMaker<>(String.class);
String[] strings = arrayMaker.creat(10);
System.out.println(Arrays.toString(strings));
}
}
输出结果:
[null, null, null, null, null, null, null, null, null, null]
这段代码的点在于Array.newInstance(kind, size);为啥我们传入了Class类型,还有要创建的size,这个方法还是给我们返回了Object,还需要我们使用强制类型转换?
-
类型擦除的限制
Java的泛型在编译后会被擦除为Object或边界类型(如Comparable)。虽然ArrayMaker的kind参数通过Class保留了类型信息,但Array.newInstance的返回类型设计为Object,因为它需要支持所有可能的componentType(如String.class、Integer.class等),无法在编译时静态绑定到具体类型。 -
数组的协变性
Java数组是协变的(如String[]可视为Object[]),但泛型是不变的(如List不能视为List)。当通过反射创建数组时,返回的实际类型(如String[])在运行时是明确的,但编译器无法验证其与泛型类型T[]的兼容性,因此需要显式强制转换。 -
安全性责任转移给开发者
Array.newInstance的设计要求开发者通过Class参数确保类型安全。例如,若传入String.class,则返回的数组实际类型是String[],但编译器无法自动推断这一点。开发者需通过强制转换(T[])告知编译器这一安全操作,并自行保证kind与T的一致性。
泛型容器
public class ListMaker<T> {
List<T> create() {
return new ArrayList<>();
}
public static void main(String[] args) {
ListMaker<String> stringListMaker = new ListMaker<>();
List<String> list = stringListMaker.create();
}
}
如何解决擦除导致的类型丢失
其实泛型,本身就是为了,让我们的代码能够处理多个类,让代码更加具有泛化能力。既然是跨多个类,那么实际上,我们就无法得知,运行时具体传入的是哪种类型,类型在编译期间就被擦除掉了。
那么如何解决这个问题呢? 那就是再额外把类型的Class对象传入进来。