java泛型-类型擦除

本文详细介绍了Java中的泛型和类型擦除概念,包括无限制和有限制的类型擦除在变量和方法上的应用,并通过实例解析了桥接方法的生成原因。通过类型擦除,Java编译器在编译时将泛型转换为原始类型,以确保与早期版本的兼容性。同时,文章展示了如何通过反射API观察类型擦除的影响。
摘要由CSDN通过智能技术生成


1.泛型和类型擦除

泛型:泛型是java 5中引入的一个新特性,允许我们在定义类和接口的时候使用类型参数,类型参数在使用时用具体的类型替换,所以为了兼容java 5之前的版本,引入了类型擦除
类型擦除:编译器在编译时擦除了所有跟类型参数相关的信息,所以在运行时访问不到类型参数相关的信息,编译器在编译时会将类型参数转换为原始类型,目的就是为了兼容java 5之前的版本,类型擦除主要的过程是首先移除所有类型参数,然后用其最顶级的父类型替换

2.变量类型擦除

2.1 无限制类型擦除

编译器在编译时将类型参数T替换为了Object,所以在运行时我们打印成员变量t的类型时访问到的是Object类型

import java.lang.reflect.Field;

public class Test01 {
    public static void main(String[] args) {
        // 无限制类型擦除
        Class<A> a = A.class;
        Field[] fields = a.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field.getName()+":"+field.getType().getSimpleName()); // t:Object
        }
    }
}

class A<T> {
    T t;
}

2.2 有限制类型擦除

编译器在编译时将类型参数T替换为了Number(由于T extends Number,所以T的上限是Number,所以T最顶级的父类型是Number),所以在运行时我们打印成员变量t的类型时访问到的是Number类型

import java.lang.reflect.Field;

public class Test02 {
    public static void main(String[] args) {
        // 有限制类型擦除
        Class<B> b = B.class;
        Field[] fields = b.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field.getName()+":"+field.getType().getSimpleName()); // t:Number
        }
    }
}

class B<T extends Number> {
    T t;
}

3.方法类型擦除

3.1 无限制类型擦除

编译器在编译时将类型参数K替换为了Object,所以在运行时我们打印成员方法fun的返回值类型时访问到的是Object类型

import java.lang.reflect.Method;

public class Test03 {
    public static void main(String[] args) {
        // 无限制类型擦除
        Class<C> c = C.class;
        Method[] methods = c.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method.getName()+":"+method.getReturnType().getSimpleName()); // fun:Object
        }
    }
}

class C<T extends Number> {
    T t;

    public <K> K fun(K k) {
        return null;
    }
}

3.2 有限制类型擦除

编译器在编译时将类型参数K替换为了Number(由于K extends Number,所以K的上限是Number,所以K最顶级的父类型是Number),所以在运行时我们打印成员方法fun的返回值类型时访问到的是Number类型

import java.lang.reflect.Method;

public class Test04 {
    public static void main(String[] args) {
        // 有限制类型擦除
        Class<D> d = D.class;
        Method[] methods = d.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method.getName()+":"+method.getReturnType().getSimpleName()); // fun:Number
        }
    }
}

class D<T extends Number> {
    T t;

    public <K extends Number> K fun(K k) {
        return null;
    }
}

3.3 桥接方法

首先我们来分析以下代码,左边定义了Info接口和对应的实现类,实现类指定泛型具体类型为Integer并且重写了抽象方法info,右边当编译器编译时执行类型擦除后Info接口中的类型参数T被替换成了Object类型,这时我们会发现Info接口中的抽象方法info的参数类型和返回值类型均为Object,对应的实现类中的方法info的参数类型和返回值类型均为Integer,我们都知道重写必须满足参数列表相同,但是此时不满足,所以就有了桥接方法的出现,通过桥接方法保持接口和类的实现关系
在这里插入图片描述

在理解完上图中的代码逻辑后,相信大家能看懂下面这段代码的执行结果(先打印fun:Integer,后打印fun:Object)

import java.lang.reflect.Method;

public class Test05 {
    public static void main(String[] args) {
        Class<IImpl> ii = IImpl.class;
        Method[] methods = ii.getDeclaredMethods();
        for (Method method : methods) {
            // fun:Integer
            // fun:Object
            System.out.println(method.getName()+":"+method.getReturnType().getSimpleName());
        }
    }
}

interface I<T> {
    T fun();
}

class IImpl implements I<Integer> {
    @Override
    public Integer fun() {
        return null;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值