Java泛型擦除

Java泛型擦除

什么是泛型擦除?

       首先了解一下什么是泛型?我个人的理解:由于集合中可以存储任意类型的对象,但是集合中最先存储的对象类型一旦确定后,就不能在存储其他类型的对象了,否则,编译时不会报错,但运行时会抛出ClassCastException异常。为解决此问题,引入泛型,指定该集合对象中存储的对象类型,这样一旦该集合中存储了其他的对象类型,编译时会提示错误!(理解不当之处,希望大神多指点吐舌头)。

        泛型的擦除就是说,在编译的时候集合中指定了确定的对象类型,但在运行时将集合中可以存储的该对象类型擦除了!举例来说的话就是:

[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. import java.awt.List;  
  2. import java.util.ArrayList;  
  3. public class GenericDemo {  
  4.   
  5.     public static void main(String[] args) {  
  6.         ArrayList<String> a = new ArrayList<String>();  
  7.         // 集合a中只能存储String类型的对象  
  8.         a.add("abc");  
  9.         a.add(6);   // 报错.运行时注释掉  
  10.           
  11.         ArrayList<Integer> b = new ArrayList<Integer>();  
  12.         // 集合b中只能存储Integer类型的对象  
  13.         b.add(6);  
  14.         b.add("abc");   // 报错.运行时注释掉  
  15.           
  16.         System.out.println(a.getClass() == b.getClass());  
  17.         // 输出结果为    true  
  18.         /*  
  19.          *  原因:泛型运行时具有擦除功能;ArrayList<String>和ArrayList<Integer>擦除  
  20.          *  后的类型为ArrayList  
  21.          */  
  22.     }  
  23. }  
每个类都有一个class属性,该例子同时说明使用泛型不会改变class属性的返回值。

注意点:Java不能创建泛型数组:因为Java中的数组都是通过反射动态建立的,在运行时,泛型会擦除类型信息,所以没有了类型信息,当然也就不能构造出数组了。

以上言语措辞或对问题的认识上有什么不当的,希望路过大神指教!

<think>嗯,用户想了解Java擦除的原理和机制。首先,我需要回忆一下Java的基本概念。Java 5引入的特性,用于在编译时提供类检查,避免类转换错误。但Java是通过类擦除实现的,这和C++的模板不一样。 接下来,用户提到的引用内容里有几个关键点。比如引用1提到,擦除时会擦除所有声明的地方,如果指定了上界,就用上界替换,否则用Object。还有通过反射绕过检查的例子,这说明运行时信息不存在了。引用5也提到,在运行时会被擦除为原始类,比如List<String>变成List。 那擦除的原理是什么?首先,编译时检查类,但生成的字节码中不保留参数,替换为边界或Object。例如,List<String>在编译后变成List,元素类是Object。但编译器会在必要的地方插入类转换代码,比如获取元素时自动转换为String。这样做的原因可能与兼容性有关,如引用4提到的,Java选择了第二种方案,直接化旧集合类,保持兼容性,避免破坏现有代码。 用户可能还想知道类擦除带来的影响。比如无法在运行时获取的具体类,因为反射时只能看到原始类。例如引用1中的例子,通过getTypeParameters()只能得到占位符E、K、V,而无法得到实际的Integer或String。但有些情况可以通过反射技巧获取,比如方法参数中的信息,可能保存在签名中。 另外,擦除可能导致一些限制,比如不能创建数组,因为运行时类信息不足。还有重载方法时,如果参数类擦除后的相同类,会导致编译错误。 用户的问题可能还包括如何验证擦除。引用1中提到了两种方式:通过Class对象的getTypeParameters方法,或者通过反射绕过编译时的类检查。比如向ArrayList<String>添加Integer元素,这在编译时不允许,但通过反射调用add方法,参数类为Object,就可以绕过,说明运行时类检查不存在。 总结一下,回答需要涵盖擦除的定义、实现原理、验证方法、设计原因以及带来的影响。同时要结合用户提供的引用内容,比如兼容性考虑、反射的例子等,确保引用正确。</think>### Java擦除的原理与机制 #### 一、擦除的定义 Java擦除(Type Erasure)指**编译器在编译阶段移除信息**,将替换为原始类(Raw Type)或上界(若指定)。这一机制使得代码在运行时仅保留原始类信息,确保与旧版本Java代码的兼容性[^4]。 例如,代码`List<String>`在编译后会被擦除为`List`,而`List<T extends Number>`会被替换为`List<Number>`。 #### 二、擦除的实现原理 1. **编译阶段类检查** 编译器在编译时检查约束,确保类安全。例如,`List<String>`中只能添加`String`类元素,否则报错。 2. **字节码生成阶段擦除** 生成的字节码中,所有参数(如`T`、`E`)被替换为: - 若指定上界(如`T extends Number`):替换为上界类`Number`; - 若未指定上界:替换为`Object`[^1]。 示例: ```java // 编译前 public class Box<T> { private T value; public void set(T value) { this.value = value; } } // 编译后(擦除为Object) public class Box { private Object value; public void set(Object value) { this.value = value; } } ``` 3. **桥接方法生成** 若类/方法涉及继承或接口实现,编译器会生成桥接方法(Bridge Method)来保持多态性。例如: ```java // 编译前 class MyList implements List<String> { @Override public boolean add(String s) { /*...*/ } } // 编译后生成桥接方法 class MyList implements List { public boolean add(Object o) { return add((String) o); } // 桥接方法 } ``` #### 三、擦除的验证方法 1. **通过反射绕过类检查** 运行时信息已擦除,可通过反射向`List<String>`添加非`String`类元素: ```java List<String> list = new ArrayList<>(); list.add("test"); Class<?> clazz = list.getClass(); Method addMethod = clazz.getMethod("add", Object.class); // 参数类为Object addMethod.invoke(list, 123); // 添加Integer类 System.out.println(list.get(1)); // 输出:123[^1] ``` 2. **通过Class对象获取占位符** ```java List<Integer> intList = new ArrayList<>(); TypeVariable<?>[] typeParams = intList.getClass().getTypeParameters(); System.out.println(Arrays.toString(typeParams)); // 输出:[E][^1] ``` #### 四、擦除的设计原因 1. **兼容性需求** Java选择直接化原有集合类(如`ArrayList`),而非新增集合类,以实现新旧代码平滑过渡[^4]。 2. **运行时效率** 擦除机制避免了为每个生成新类,减少内存占用和类加载开销。 #### 五、擦除的局限性 1. **无法获取运行时** 例如,无法通过`list instanceof List<String>`检查具体类[^5]。 2. **类擦除导致的重载冲突** ```java // 编译报错:两个方法擦除后均为`method(List)` void method(List<String> list) {} void method(List<Integer> list) {} ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值