从头认识java-13.6 类型擦除(type erasure)

这一章节我们讨论一下类型擦除。

1.什么是类型擦除?

在java里面泛型其实是伪泛型,因为他都只是在编译器里面实现的,在jvm里面是没有泛型的概念。

我们看下面的代码:

package com.ray.ch11;

import java.util.ArrayList;

public class Test {
	public static void main(String[] args) {
		ArrayList<Integer> list1 = new ArrayList<Integer>();
		ArrayList<String> list2 = new ArrayList<String>();
		System.out.println(list1.getClass() == list2.getClass());
	}
}

输出:

true


为什么两个list对应的Class对象是一致的呢?因为在jvm层面,他只看到List,没有里面的类型。

也就是,泛型里面的类型信息被擦掉了,这就是所谓的类型擦除。

 

2.扩展

我们使用另一个例子来扩展开来。

package com.ray.ch11;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;

public class Test {
	public static void main(String[] args) {
		ArrayList<Integer> list = new ArrayList<Integer>();
		HashMap<String, String> map = new HashMap<String, String>();
		HashSet<String> set = new HashSet<String>();
		System.out
				.println(Arrays.toString(list.getClass().getTypeParameters()));
		System.out.println(Arrays.toString(map.getClass().getTypeParameters()));
		System.out.println(Arrays.toString(set.getClass().getTypeParameters()));
	}
}


输出:

[E]
[K, V]
[E]


从上面的输出,我们只是看见泛型的标记,但是完全不问清楚里面的类型信息,这是因为代码经过编译器后,类型信息被擦除了。

 

总结:这一章节我们引出类型擦除的概念。

 

这一章节就到这里,谢谢。

-----------------------------------

目录


 

<think>嗯,用户之前问过Java和C++语法区别,现在接着问Java类型擦除。看来他对Java的泛型机制不太清楚,特别是和C++模板的区别。类型擦除Java泛型实现的一个核心点,可能他在实际编程中遇到了泛型相关的问题,或者想深入理解Java的设计选择。 我需要先确认类型擦除的定义,然后解释为什么Java要用类型擦除,比如为了兼容旧版本,保持字节码兼容。然后要举例子说明类型擦除的具体表现,比如编译后的类型信息丢失,无法在运行时获取泛型参数类型。可能还需要对比C++的模板,因为用户之前的问题涉及到两者的比较,这样他能更好地理解两者的差异。 另外,用户可能想知道类型擦除带来的限制,比如不能创建泛型数组,instanceof无法使用,以及重载的问题。这些实际开发中的痛点需要具体例子说明。同时,要提到绕过类型擦除的方法,比如通过传递Class对象,这样用户知道如何处理类型信息丢失的情况。 还要注意用户的技术水平,他可能已经有一定编程经验,但可能对底层机制不熟悉,所以需要用简洁明了的语言,避免过于技术化的术语,但又要准确。可能需要用代码示例来辅助解释,比如ArrayList<Integer>和ArrayList<String>在运行时的类型是一样的,这样更直观。 最后总结类型擦除的意义,优缺点,让用户理解Java设计的选择背后的权衡。可能用户想了解为什么Java不采用C++那样的具体化泛型,所以需要提到兼容性和迁移成本的问题。</think>以下是关于 Java 类型擦除Type Erasure)的详细说明,从设计原理到实际影响逐步解析: --- ### 一、**什么是类型擦除?** 类型擦除Java 泛型(Generics)的核心实现机制。**编译时保留泛型类型信息,但在编译后会将泛型类型替换为原始类型(Raw Type)或边界类型(如 `Object`),并插入强制类型转换**。这一设计是为了兼容 Java 5 之前的旧版本代码。 **示例**: ```java // 编译前代码 List<String> list = new ArrayList<>(); list.add("Hello"); String s = list.get(0); // 编译后等效代码(类型擦除后) List list = new ArrayList(); // 原始类型 list.add("Hello"); String s = (String) list.get(0); // 强制转换 ``` --- ### 二、**类型擦除的具体表现** #### 1. **泛型类型信息丢失** - 运行时无法获取泛型参数的具体类型: ```java List<Integer> intList = new ArrayList<>(); List<String> strList = new ArrayList<>(); // 输出均为 true(运行时类型相同) System.out.println(intList.getClass() == strList.getClass()); ``` #### 2. **泛型类型参数替换规则** - 无界类型参数(如 `<T>`)替换为 `Object`: ```java // 编译前 public class Box<T> { private T value; } // 编译后等效 public class Box { private Object value; } ``` - 有界类型参数(如 `<T extends Number>`)替换为边界类型: ```java // 编译前 public class Box<T extends Number> { private T value; } // 编译后等效 public class Box { private Number value; } ``` --- ### 三、**类型擦除带来的限制** #### 1. **无法创建泛型数组** ```java // 编译错误:Generic array creation T[] array = new T[10]; ``` #### 2. **无法使用 `instanceof` 检查泛型类型** ```java if (list instanceof List<String>) { ... } // 编译错误 ``` #### 3. **重载冲突** ```java // 编译错误:方法签名冲突(擦除后均为 List<Object>) public void print(List<String> list) {} public void print(List<Integer> list) {} ``` #### 4. **无法直接实例化泛型类型** ```java public class Box<T> { private T obj = new T(); // 编译错误:类型擦除后 T 变为 Object } ``` --- ### 四、**如何绕过类型擦除的限制** #### 1. **显式传递 `Class` 对象** ```java public class Box<T> { private Class<T> type; public Box(Class<T> type) { this.type = type; } public T createInstance() throws Exception { return type.getDeclaredConstructor().newInstance(); } } ``` #### 2. **通过反射获取泛型信息(有限场景)** ```java // 获取父类泛型参数类型(需继承带泛型的父类) public class StringList extends ArrayList<String> {} Type genericType = StringList.class.getGenericSuperclass(); if (genericType instanceof ParameterizedType) { Type[] types = ((ParameterizedType) genericType).getActualTypeArguments(); System.out.println(types[0]); // 输出 class java.lang.String } ``` --- ### 五、**类型擦除的意义与争议** #### **优点**: - **兼容性**:确保泛型代码与旧版本 JVM 兼容。 - **性能**:运行时无需维护泛型类型信息,减少开销。 #### **缺点**: - **灵活性降低**:部分泛型高级特性无法实现(如具体化类型)。 - **代码冗余**:需手动处理类型转换(如集合类)。 --- ### 六、**与 C++ 模板的对比** | 特性 | Java 泛型(类型擦除) | C++ 模板 | |------------------|------------------------------------------|-----------------------------------| | **实现机制** | 编译时擦除类型,运行时无类型信息 | 编译时生成具体类型代码(代码膨胀)| | **类型检查** | 编译时检查,运行时无泛型类型 | 编译时生成类型安全检查 | | **性能** | 无额外优化(依赖强制转换) | 可生成高度优化的机器码 | | **灵活性** | 受限于类型擦除 | 支持模板元编程等复杂特性 | --- ### 总结 类型擦除Java 泛型实现的核心机制,**在保证兼容性的同时牺牲了部分运行时灵活性**。理解这一机制有助于避免泛型编程中的常见陷阱(如类型转换错误),并合理利用反射等工具突破限制。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值