泛型,泛型擦除,T extends class

本文探讨Java泛型的概念,类型擦除的作用,以及通过实例展示其工作原理。特别关注泛型擦除后的类型判断,并解析`<Textends`和`<?>`在类定义中的应用。

泛型,泛型擦除,T extends class

  • 泛型本质就是 参数化类型

  • 作用是提高Java程序的类型安全。并且还可以更好的代码复用性

    • 比如BaseDao,BaseService
  • 注意事项泛型不接受基本数据类型,不能有LIst这种形式出现

  • Java中的泛型是假泛型,因为在Java运行期间,所有的泛型信息都会被擦除,这个就是所谓的类型擦除。

    • 泛型擦除的实例作用:

      • List<String> l1 = new ArrayList<String>();
        List<Integer> l2 = new ArrayList<Integer>();
        		
        System.out.println(l1.getClass() == l2.getClass());
        
      • 最后的结果是true

      • 原因就是泛型信息只存在于代码编译阶段,在进入JVM之前,泛型相关信息会被擦除,只留下个list.class

  • 在了解了泛型擦除之后再看下面代码(目的就是想要一个list能够跳过编译器存储String类型的数据)

public class ToolTest {


	public static void main(String[] args) {
		List<Integer> ls = new ArrayList<>();
		ls.add(23);
//		ls.add("text");
		Method method = ls.getClass().getDeclaredMethod("add",Object.class);
			method.invoke(ls,"test");
			method.invoke(ls,42.9f);
}

  • 最后再介绍一下<T extends 数据类型><? extentds 数据类型>
    • T指定的是某一种具体的类型,这个类型是确定的
    • ?是一种未知的类型,调用方法的时候参数既可以是List也可以是List
    • 现有一个水果fruits父类,apple子类,现有一个类class A
      • 使用T extends fruits限定了泛型的上边界
      • 该类创建对象的时候限定了只能建立水果类和他的所有子类才行
      • new A or new A
<think> 我们正在讨论的是Java相关的问题。用户询问了的定义以及擦除是否一定会发生。 根据引用内容,我们得知: 引用[1]展示了定义中的类擦除:`class MyArrayList<T extends String>`在擦除后变成`class MyArrayList { String t; }`。 引用[2]说明了由于类擦除,在类中定义与父类方法名相同的方法会导致冲突(因为擦除后方法签名相同)。 引用[3]则用比喻的方式描述了擦除,并指出可以通过反射绕过的一些限制。 因此,我们可以这样回答: 1. 的定义:Java中参数化类的一种机制。它允许在定义类、接口或方法时使用类参数,在使用时再指定具体的类。这样做的目的是增强代码的类安全性和重用性。 2. 擦除:在Java中,是在编译时实现的,编译器在编译时会进行类检查,然后在生成字节码之前将信息擦除,替换为原始类(如指定了上界则为上界,否则为Object),并在必要的地方插入强制类转换。这就是擦除。 3. 擦除是否一定会发生? 在Java语言中,擦除是必然发生的。这是Java实现的特点。引用[1]和[2]都展示了擦除后的结果,引用[3]则说明了擦除带来的限制以及如何绕过。 但是,要注意的是,并非所有语言都是通过擦除实现的。例如C#的在运行时保留类信息,而Java选择擦除是为了向后兼容,使得代码能够与旧版本的Java代码和库兼容。 因此,回答用户问题:在Java中,擦除一定会发生。但在其他语言中可能不是这样。 另外,根据引用[3]提到的,我们可以通过反射绕过的一些限制,这也说明了擦除的存在(因为运行时没有类信息,所以可以绕过)。 我们需要在回答中自然地引用给出的参考文献。 结构: 一、的定义 二、擦除及其原因 三、擦除是否一定发生(在Java中是一定发生的,并解释为什么) 生成相关问题: 1. Java擦除带来了哪些限制? 2. 如何通过反射绕过Java的限制? 3. 在编译时是如何进行类检查的? </think> ### 的定义与擦除机制解析 #### 一、的定义 (Generics)是一种参数化类的编程机制,允许在定义类、接口或方法时使用**类形参**(如 `<T>`),在使用时再指定具体类(如 `<String>`)。核心价值包括: 1. **类安全**:编译时检查类匹配,避免运行时类转换错误 ```java // 非容器可能引发ClassCastException List list = new ArrayList(); list.add("text"); Integer num = (Integer) list.get(0); // 运行时错误! // 容器在编译时拦截错误 List<String> safeList = new ArrayList<>(); safeList.add(100); // 编译报错[^2] ``` 2. **代码复用**:同一套逻辑可处理多种数据类 ```java // 方法示例 public <T> T getFirst(List<T> list) { return list.get(0); } ``` 3. **消除强制转换**:自动处理类转换 ```java // 传统方式 String s = (String) list.get(0); // 方式 String s = safeList.get(0); // 自动转换 ``` #### 二、擦除机制 擦除(Type Erasure)是Java的核心实现机制,其运作原理如下图所示: $$ \begin{array}{c} \text{源代码} \\ \downarrow \\ \text{编译器} \\ \downarrow \\ \text{字节码(.class)} \\ \end{array} \quad \begin{array}{l} \text{带信息} \\ \downarrow \\ \text{擦除规则:} \\ \text{- 无界类 → Object} \\ \text{- 有界类 → 边界类} \\ \downarrow \\ \text{无信息} \\ \end{array} $$ ##### 关键特性: 1. **必然发生**:在Java擦除是**必然发生的**(在编译阶段),这是由JVM的向后兼容性决定的[^1][^3] 2. **擦除规则**: - 无界类参数 `<T>` 擦除为 `Object` ```java // 源代码 class Box<T> { T item; } // 字节码等价 class Box { Object item; } [^1] ``` - 有界类 `<T extends String>` 擦除为边界类 ```java // 源代码 class MyArrayList<T extends String> { T t; } // 字节码等价 class MyArrayList { String t; } [^1] ``` 3. **桥接方法**:编译器生成合成方法维持多态性 ```java interface Comparable<T> { int compareTo(T o); } class String implements Comparable<String> { // 编译器生成桥接方法 public int compareTo(Object o) { return compareTo((String) o); } } ``` #### 三、擦除带来的影响 1. **运行时类丢失**: ```java List<String> list = new ArrayList<>(); System.out.println(list.getClass()); // 输出 ArrayList ``` 2. **实例化限制**: ```java T obj = new T(); // 编译错误,擦除后相当于 new Object() ``` 3. **方法签名冲突**(如引用[2]示例): ```java class Pair<T> { public boolean equals(T value) { // 擦除后与Object.equals(Object)冲突[^2] } } ``` #### 四、避免擦除的替代方案 虽然Java**强制实施擦除**,但可通过以下方式保留类信息: 1. **显式传递Class对象**: ```java class Factory<T> { Class<T> type; Factory(Class<T> type) { this.type = type; } T create() { return type.newInstance(); } } ``` 2. **使用反射API**: ```java Type type = ((ParameterizedType)getClass() .getGenericSuperclass()).getActualTypeArguments()[0]; ``` > **重要结论**:在Java语言规范中,擦除是必然发生的机制,这是为实现二进制兼容性付出的代价[^3]。其他语言如C#则通过*具体化*保留运行时类信息。 --- ###
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值