介绍⼀下泛型擦除
1.是什么
泛型擦除(Type Erasure)是Java泛型实现中的一个重要概念。Java的泛型是通过类型擦除来实现的,这意味着在运行时,泛型信息(即类型参数的具体类型)是不可用的。编译器在编译时会对泛型代码进行擦除处理,将所有的泛型类型替换为它们的原始类型(raw type),通常是Object
,同时插入必要的类型转换以保持类型安全。
泛型擦除的详细解释
-
编译时与运行时:
- 编译时:Java编译器会检查泛型代码,确保类型安全。例如,它会确保你不会将
String
对象放入List<Integer>
中。 - 运行时:由于Java的泛型是通过擦除来实现的,所以在运行时,所有的泛型信息都会被擦除。JVM看到的只是原始类型,而不是泛型类型。
- 编译时:Java编译器会检查泛型代码,确保类型安全。例如,它会确保你不会将
-
类型擦除的示例:
假设我们有以下泛型类和方法:
public class Box<T> { private T t; public void set(T t) { this.t = t; } public T get() { return t; }
} public void printBoxContent(Box<?> box) { System.out.println(box.get());
}
在编译时,编译器会检查Box
类中T
类型的用法,确保它们都是安全的。但是,在编译成字节码后,所有的T
都会被替换为Object
(如果T
没有上界限制的话)。因此,上面的Box
类在运行时实际上等价于:
public class Box { private Object t; public void set(Object t) { this.t = t; } public Object get() { return t; }
}
注意:虽然类型被擦除了,但Java编译器会通过生成桥接方法(bridge methods)和类型转换来保持类型安全。
3.桥接方法:
当子类覆盖了泛型父类中的方法,并且子类的方法使用了不同的类型参数时,编译器会生成桥接方法。桥接方法是一种合成方法,它允许子类的方法能够覆盖父类中具有相同擦除签名的方法。
4.类型转换:
在泛型代码中,尽管运行时没有泛型类型信息,但编译器会插入必要的类型转换。这些类型转换在运行时执行,但由于它们是在编译时根据类型安全的规则插入的,因此不会引发ClassCastException
(除非你的代码本身就有错误)。
5.类型参数的上界:
如果泛型类型参数有上界(如T extends Number
),则擦除后的类型将是该上界的具体类型(在这个例子中是Number
),而不是Object
。
泛型擦除的影响
- 性能:由于泛型信息在运行时不可用,因此使用泛型通常不会对性能产生负面影响(相对于使用原始类型)。然而,某些情况下,由于需要额外的类型转换,可能会引入一些微小的性能开销。
- 限制:由于泛型擦除,你不能在运行时检查泛型类型参数的具体类型(即你不能使用
instanceof
来检查某个泛型对象是否是某个特定类型的实例)。此外,你也不能创建泛型类型的Class
对象(如Class<List<String>>
是不可能的,但你可以使用Class<List>
)。 - 设计考虑:了解泛型擦除对于设计泛型API和库至关重要。它可以帮助你避免常见的陷阱,如错误地假设运行时存在泛型类型信息。