Java泛型和擦除

本文介绍了Java中的泛型概念,包括泛型作为模板的使用方式,如何创建泛型类以及泛型静态方法的定义。同时,解释了泛型的通配符,如上界通配符和下界通配符的用途。此外,讨论了Java泛型的擦拭法,即在运行时泛型会被擦拭成Object类型,导致的一些限制,如不能实例化基本类型,无法获取泛型的Class等。

JAVA泛型和擦拭

泛型

1、什么是泛型

泛型可以看作是一个模板,例如:ArrayList<T>,每次用到的时候就创建对应的ArrayList<类型>

// 存储String的ArrayList:
ArrayList<String> strList = new ArrayList<String>();
// 存储Float的ArrayList:
ArrayList<Float> floatList = new ArrayList<Float>();
// 存储自定义类型Person的ArrayList:
ArrayList<Person> personList = new ArrayList<Person>();

注意<>中不能是基本数据类型

2、使用泛型

编写一个泛型

class Bird<T> {
    private T name;
    private T address;
    private T[] elem;  //泛型类型的数组
    public Bird(T name,T address) {
        this.name = name;
        this.address = address;
        this.elem = (T[])new Objects[10];   //创建数组时使用Objects类型,再强转为T[]类型
    }

    public T getName() {
        return name;
    }

    public T getAddress() {
        return address;
    }

    public void setName(T name) {
        this.name = name;
    }

    public void setAddress(T address) {
        this.address = address;
    }
}

注意:定义泛型静态方法时需要在static 后加上<T>

public static<T> Bird<T> mesg(T name,T address){
        return new Bird<>(name,address);
    }

泛型可以定义多种类型,比如我们常用的Map<K,V>就是使用了两种泛型

3、通配符

<T extends 类型>,称为上界通配符,例如:<T extends Number> 表示T的类型,只能是Number的子类或者本身,即NumberT类型的上界。

<T super 类型>表示,方法参数接受所有类型为该类型本身或该类型的父类,例如<T super Integer>,表示方法接受的类型为Integer或者Integer的父类。

擦拭法

擦拭法是指,我们定义的泛型类在执行时会被擦拭成Object类型。即在运行时,泛型类的T会被擦成Object,这就是为什么泛型类可以接收所有数据类型。所以,Java的泛型是由编译器在编译时实行的,编译器内部永远把<T>当作Object处理,但是,在需要转型的时候,编译器就会根据<T>的类型安全地强制转型

擦拭法地局限性

  • <T>不能是基本类型,因为实际类型是ObjectObject类型无法持有基本类型。
  • 无法取得带泛型的class。因为T是Object,我们对Bird<Integer>Bird<String>使用getClass()方法时,返回的是Bird类的class。因为他们编译之后都是Bird<Object>
  • 无法判断带泛型的类型,即不能使用instanceof来判断一个对象是不是泛型类的实例。因为并不存在Bird<Integer>.calss ,只存在Bird.calss
  • 不能实例化T类型,即new T()
### 三级标题:Java擦除机制详解 Java擦除机制是指在编译阶段将参数替换为其边界(如 `T extends Number` 则替换为 `Number`)或 `Object` 。这一机制使得代码在运行时无需保留参数的具体信息,从而保持与 Java 1.5 之前版本的兼容性,同时也减少了代码的冗余[^1]。 在代码中,编译器会在编译期间对进行检查,并在生字节码时将所有参数替换为 `Object` 或其上界。例如,一个 `MyArray<T>` 在编译后的字节码中将不再包含 `T`,而是全部替换为 `Object`。这种机制确保了运行时加载器虚拟机无需感知的存在[^3]。 例如,以下在编译后将被擦除为非形式: ```java public class Box<T> { private T value; public void set(T value) { this.value = value; } public T get() { return value; } } ``` 经过擦除后,生的字节码大致等价于: ```java public class Box { private Object value; public void set(Object value) { this.value = value; } public Object get() { return value; } } ``` 擦除机制在运行时会带来一些限制,例如无法直接实例化数组。由于信息在运行时被擦除,JVM 无法知道具体的数组,因此 `new T[10]` 会导致编译错误。这种限制源于 Java 数组的协变性与擦除之间的冲突,使得数组的创建在语言层面被禁止[^4]。 此外,擦除机制也影响了运行时的信息获取。例如,通过反射检查的字段或方法时,无法直接获取其参数的具体。反射 API 提供的 `getGenericTypes` 方法可以在一定程度上获取信息,但这些信息仅在编译时保留,并在运行时通过 `Class` 对象的附加元数据进行访问[^2]。 --- ### 三级标题:擦除对程序行为的影响 擦除机制虽然提高了 Java 的兼容性代码复用性,但也带来了一些设计上的挑战。例如,在运行时无法判断的实际,导致一些运行时检查动态代理等高级特性在使用时需要额外处理。此外,由于擦除的存在,方法的重载规则也受到限制,避免出现冲突歧义。 在异常处理方面,Java 不允许使用作为 `catch` 块的参数。例如,以下代码是非法的: ```java public class GenericException<T> extends Exception { // ... } try { // some code } catch (GenericException<String> e) { // compile error } ``` 这种限制同样源于擦除机制,使得 JVM 在运行时无法识别的异常。 --- ### 三级标题:总结 Java擦除机制在编译阶段将参数替换为 `Object` 或其上界,从而实现代码的运行时兼容性代码复用性。尽管这一机制带来了安全简化开发的优势,但也导致了一些限制,例如无法实例化数组、无法获取运行时信息以及异常的使用受限等问题。 的设计目标是提高代码的可读性安全性,而擦除则是在语言演化过程中为保持兼容性所做出的权衡[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值