成都成仁路网站建设,如何注册一个自己的网站,北京做网站建设价格低,知名的饰品行业网站开发Java 泛型深入底层原理解析#xff1a;类型擦除与桥方法的真相
一、Java中的伪泛型
Java 从 JDK 1.5 引入泛型之后#xff0c;大大提升了代码的类型安全性与可读性。但泛型的底层实现并不像 C 的模板机制那样是“真正的泛型”#xff0c;Java 的泛型是伪泛型#xff0c;在…Java 泛型深入底层原理解析类型擦除与桥方法的真相
一、Java中的伪泛型
Java 从 JDK 1.5 引入泛型之后大大提升了代码的类型安全性与可读性。但泛型的底层实现并不像 C 的模板机制那样是“真正的泛型”Java 的泛型是伪泛型在编译后会进行类型擦除这带来了很多容易忽略的陷阱。
这篇文章将主要介绍Java 泛型是如何通过擦除实现泛型在编译期与运行期发生了什么为什么会出现桥方法以及通过反射获取泛型信息 二、泛型基本语法
public class BoxT {private T value;public void set(T value) { this.value value; }public T get() { return value; }
}我们可以通过如下方式使用它
BoxString stringBox new Box();
stringBox.set(Hello);
// 编译器知道 stringBox 中只能放 String看似“类型安全”但实际运行时情况并不是这样。因为运行时由于类型擦除机制泛型的类型信息被移除导致实际运行中并不能真正限制类型看下面这段代码因为编译时期进行了类型擦除因此反射时jvm并不知道list中只允许添加String
ListString list new ArrayList();
list.add(hello);// 通过反射绕过泛型限制
Method addMethod list.getClass().getMethod(add, Object.class);
addMethod.invoke(list, 123); // 添加了一个 IntegerSystem.out.println(list); // 输出: [hello, 123]三、类型擦除T 到底去哪了
Java 的泛型是编译期语法糖在编译过程中T 会被替换成其限定类型上限。如果没有显式指定则默认是 Object。看下面的例子
public class BoxT {T value;void set(T value) { this.value value; }T get() { return value; }
}编译后实际变成了
public class Box {Object value;void set(Object value) { this.value value; }Object get() { return value; }
}这就意味着泛型信息在运行时根本就不存在这就是类型擦除。 四、类型擦除带来的限制与问题
1. 泛型不能用于基本类型
Boxint box; 因为泛型最终会被擦除为 Object而基本类型不能直接赋值给 Object需要装箱autoboxing。
2. 泛型不能创建数组
T[] arr new T[10]; // 编译错误擦除后不知道 T 是什么类型无法分配合适的数组类型。
3. 泛型类无法通过 instanceof 判断类型参数
if (box instanceof BoxString) {} // 编译错误因为泛型信息在运行期被擦除了JVM 无法判断类型。 五、桥方法擦除下的多态陷阱
当子类重写带泛型的方法时由于擦除后签名可能不同(子类重写父类方法后一般要求参数类型和数量不变因为底层要生成一个方法签名这部分内容涉及到多态动态链接的知识)为了保证运行时的多态性编译器会自动生成桥方法。例如下面这段代码
class ParentT {T get() { return null; }
}class Child extends ParentString {OverrideString get() { return hello; }
}在实际编译后为
class Child extends Parent {// 桥方法Object get() {return get(); // 调用下面的真实方法}// 实际的 get 方法String get() {return hello;}
}桥方法保证了子类方法可以在擦除后继续覆盖父类的方法维护多态特性。 六、反射获取泛型信息
虽然泛型信息在运行时被擦除但某些泛型信息仍保留在 class 文件的元数据中可以通过反射读取。这部分简单介绍一下如下代码
class GenericHolderT {}class StringHolder extends GenericHolderString {}public static void main(String[] args) {Type superClass StringHolder.class.getGenericSuperclass();System.out.println(superClass);
}总结
Java泛型通过类型擦除机制在编译时期提供类型安全但运行时类型信息会被擦除因此在反射、数组等场景时需要格外注意