泛型的擦除和转换

---------------------- android培训java培训、期待与您交流! ----------------------

 

泛型注意:擦除和转换
 在严格的泛型代码里,带泛型声明的类总是应该带着类型参数,但为了与老的java代码保持一致,也允许在使用带泛型声明的类时不指定类型参数。如果没有为这个泛型类指定类型参数,则该类型参数被称作一个raw type(原始类型),默认是声明该参数时指定的第一个上限类型。当把一个具有泛型信息的对象赋给另一个没有泛型信息的对象时,则所有在<>之间的类型信息都被扔掉了。比如说一个List<String>类型被转换为List,则该List对集合元素的类型检查编程了类型变量的上限(即Object)。

import java.util.List;
import java.util.ArrayList;

class Demo<T extends Number>
{
	T size;
	public Demo(){}
	public Demo(T size)
	{
		this.size = size;
	}
	public void setT(T size)
	{
		this.size = size;
	}
	public T getSize()
	{
		return this.size;
	}
}


 测试:

class TestEraser
{
	public static void main(String[] args)
	{
		//method1();
		method2();
		//method3();
	}
	
	/**
	把一个d赋给不带泛型信息的d2变量时,编译器会流失d对象的泛型信息,但因为Demo类型的参数类型上限是Number类,
	所以编译器依然知道d2.getSize()的返回值类型是Number类型。
	*/
	public static void method1()
	{
		Demo<Integer> d = new Demo<>(6);
		Integer id = d.getSize();
		
		Demo d2 = d; //把d对象赋给Demo类型的变量,会丢失泛型信息
		
		//Integer id2 = d2.getSize();  //编译错误
		Number id2 = d2.getSize();
	
	}
	
	/**
		下面程序中定义了一个List<Integer>对象,当把对象li赋给一个不带泛型的List变量list后,编译器
		就会流失前者的泛型信息,这就是擦除。但java允许直接把List对象赋给一个List<Type>(Type可以是
		任何类型)的变量。
		从逻辑上看,List<String> 是list的子类,如果直接把一个List对象赋给一个List<String>对象
		将引发编译错误,但对于泛型而言,可以直接把一个List对象赋给一个List<String>对象,编译器
		仅仅提示“未经检查的转换”,但要访问List<String>对象中的元素将会引发ClassCastException异常
	
	*/
	public static void method2()
	{
		List<Integer> li = new ArrayList<>();
		li.add(6);
		li.add(9);
		List list = li;
		List<String> ls = list; //警告,编译运行完全正常
		//但要访问ls集合里的元素,将会引起运行时异常
		System.out.println(ls.get(0));	
		
	}
	/**
		method2代码虽然可以编译通过,但实际上对list变量实际上引用的是List<Integer>集合,所以当试图
		把集合里的元素当成String类型的对象取出时,将引发ClassCastException类型转换异常,上面的代码
		可以转换成下面的代码就好理解了。
	*/

	public static void method3()
	{
		List li = new ArrayList();
		li.add(6);
		li.add(9);
		//运行异常
		System.out.println((String)li.get(0)); 
	}
}


其实用多态来解释比较容易,编译时只看左边,即对象变量的定义,运行时看右边,无论对象的引用如何变化,左边定义的变量都存放在栈内存中,而实际访问的是堆内存中的数据。

 

---------------------- android培训java培训、期待与您交流! ----------------------详细请查看:http://edu.youkuaiyun.com/heima 

<think>好的,我现在要回答用户关于Java擦除的问题。首先,我需要整理一下已有的引用资料,确保理解正确。用户提供了五个引用,内容涉及擦除的原理、实现方式、擦除规则、影响以及相关的限制解决方案。 首先,擦除的原理是什么?根据引用2引用3,Java的在编译时会被擦除,替换为原生类,并在需要的地方插入强制转换。这意味着在运行时,信息不存在,比如ArrayList<String>ArrayList<Integer>在运行时都是同一个类。 接下来,擦除的规则。引用1引用4提到有无界擦除、上界擦除下界擦除。例如,无界的参数会被替换为Object,有上界的替换为上界类,有下界的可能替换为Object。需要确认这些规则的具体情况,以及如何验证这些规则,比如通过反射或者查看字节码。 然后是影响部分。用户可能关心擦除带来的问题,比如类信息在运行时不可用,导致无法直接实例化,或者无法进行类检查。引用4中提到了两个经典问题:在类中获取实际类接口回调中获取类,可能需要通过反射技巧来解决,比如获取父类的参数化类。 另外,引用5提到原始类不支持,比如不能使用List<int>,而必须用包装类,这会带来自动装箱的开销。同时,项目Valhalla可能在未来解决这个问题,但现在还是限制。 现在需要将这些信息组织成结构清晰的回答,分点解释原理、影响,并举例说明。同时,确保使用正确的中文表达LaTeX格式,比如代码块用反引号,数学公式用$...$,独立公式用$$...$$。不过根据用户提供的示例,似乎数学公式的例子并不直接相关,可能在这个问题中不需要使用数学公式,但需要遵循格式要求。不过用户的问题是关于Java,可能涉及代码示例,所以需要正确使用代码块的格式。 最后生成相关问题,可能包括擦除的优缺点,如何绕过擦除限制,桥接方法的作用,类擦除与类安全的冲突,以及原始类不支持的原因等。 需要检查引用是否正确标注,比如引用[^1]到[^5]是否正确对应提供的资料。根据用户提供的引用,引用1到5分别对应不同的内容,需要确保在回答中正确引用,比如提到桥接方法时引用,提到擦除原理时引用[^2][^3],原始类限制引用[^5]等。 现在组织回答的结构: 1. 擦除的原理:编译时擦除,替换为原生类,插入强制转换。 2. 擦除规则:无界、上界、下界擦除的具体情况。 3. 主要影响: - 运行时类信息丢失 - 类检查限制 - 桥接方法的作用 - 原始类不支持 4. 解决方案技巧:反射获取信息,桥接方法等。 5. 示例代码:比如类擦除后的代码示例。 6. 相关问题。 在回答过程中,需要确保每个引用的位置正确,比如提到桥接方法时引用,提到擦除实现引用[^2],原始类问题引用。 另外,用户可能对类擦除带来的类安全问题有疑问,比如为什么Java选择这种方式,可能与兼容性有关,保持与旧版本字节码的兼容性,引用2提到这是为了作为语法糖实现,确保兼容性。 可能还需要提到类擦除导致的一些常见错误,比如无法创建数组,或者instanceof无法使用,这些可以结合引用中的例子说明。 检查是否有遗漏的重要点,比如桥接方法在保持多态性的作用,引用1中的演示案例可能需要解释,但在回答中可能需要简化。 最后,确保回答符合用户的要求:结构清晰,分步骤解释,真实可靠,引用正确,使用中文,代码块正确,生成相关问题。</think>### Java擦除原理及影响解析 #### 一、擦除原理 Java通过**类擦除(Type Erasure)**实现,其核心规则为: 1. **编译期擦除**:信息在编译后被替换为原始类(如`Object`或指定上界类),并插入强制类转换代码。 - 无界类(如`<T>`)擦除为`Object`,例如`List<T>`变为`List`。 - 有上界的类(如`<T extends Number>`)擦除为上界类`Number`。 - 有下界的类(如`<? super Integer>`)擦除为`Object`。 2. **运行时无信息**:类在运行时会丢失类参数信息,例如`List<String>`与`List<Integer>`在JVM中均为`List`。 ```java // 编译前 List<String> list = new ArrayList<>(); // 编译后(通过反编译查看) List list = new ArrayList(); // 类参数被擦除 String s = (String) list.get(0); // 插入强制转换 ``` #### 二、主要影响 1. **运行时类限制** - **无法通过反射获取**:例如`new ArrayList<String>().getClass()`无法得知`String`类参数[^4]。 - **无法直接实例化对象**:`new T()`会编译报错,因为实际类未知[^3]。 2. **类安全检查局限性** - 转换错误可能在运行时才暴露。例如将`List<Integer>`强制转换为`List<String>`时,编译期无法检测。 3. **桥接方法(Bridge Method)** - 为实现多态性,编译器会生成桥接方法。例如类继承时,子类需通过桥接方法覆盖父类方法[^1]。 4. **原始类不支持** - 如`List<int>`不合法,需使用包装类`Integer`,导致自动装箱/拆箱的性能开销。 #### 三、解决方案示例 1. **获取实际类** 通过反射获取父类或接口参数: ```java public class MyList<T> { public Class<T> getType() { return (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]; } } ``` 2. **绕过实例化限制** 使用工厂方法传递`Class<T>`参数: ```java public static <T> T createInstance(Class<T> clazz) throws Exception { return clazz.getDeclaredConstructor().newInstance(); } ``` ####
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值