Java泛型中的类型擦除

Java在编译期间,所有的泛型信息都会被擦掉,反编译后会多了一个最后一行的签名文件Signature,保存到就是泛型的信息。那么之后JVM之后又是如何来判断我所需要的类型方法或者说提供的类型参数?

public class Pair <T>{
    private T first;
    private T second;

    public Pair(T first, T second) {
        this.first = first;
        this.second = second;
    }
}
class Test
    public static void main(String[] args){
        Manager CEO = new Manager("CEO", 5000, 2002, 1, 1);
        Manager CFO = new Manager("CFO", 6000, 2000, 1, 9);
        Pair<Manager> managerPair = new Pair<>(CEO, CFO);
    }

就拿这个方法来说,类型擦除后就是

public class Pair {
    private Object first;
    private Object second;

    public Pair(Object first, Object second) {
        this.first = first;
        this.second = second;
    }
}
class Test
    public static void main(String[] args){
        Manager CEO = new Manager("CEO", 5000, 2002, 1, 1);
        Manager CFO = new Manager("CFO", 6000, 2000, 1, 9);
        Pair managerPair = new Pair(CEO, CFO);
    }

它之后又是怎么创建我需要的类型参数的?
下面为什么不能通过,类型消除后Pair<manager>变成了Pair原始类型,那么相应的里面的实例字段也变成Object类型,编译后虽然类型消除了变成原始类型,但后面虚拟机会通过某种方式转换成Manager,这样岂不是和强制类型转换一样;那么既然用了强制类型转换,我就有第二种想法,类型消除后,lowlyEmployee是能转换成Object,然后Object转换成Manager,这两种都用到了强制类型转换,为什么后者不允许.........(题主刚入门,当然这说法是建立在如果我想的是正确的情况下,有点乱,放下代码示意图,如果类型消除真是这样,那么岂不是Pair<T> pair = public Pair(T first, T second)方法里无论传入的是T的子类对象还是父类对象都应该可以的吗?)

        Manager manager = new Manager("CEO", 5000, 2002, 1, 1);
        Object first =  manager;
        Manager M1 = (Manager) first;

        Employee lowlyEmployee = new Employee("zhang san", 1000, 2001, 2 ,1);
        Object second = lowlyEmployee;
        Manager M2 = (Manager) second;
public static void main(String[] args) {
        Employee lowlyEmployee = new Employee("zhang san", 1000, 2001, 2 ,1);
        Manager manager = (Manager) lowlyEmployee;
        Pair<Manager> pair = new Pair<Manager>(lowlyEmployee, lowlyEmployee);  //error
        //Pair<Manager> pair = new Pair<Manager>(manager, manager);  ok
        //Pair<Employee> pair = new Pair<Employee>(manager, manager); ok
    }
阅读 3.7k
3 个回答

显然你没理解泛型的作用。
JVM在运行时不需要判断类型。直接强制转换,为什么呢?因为编译时确保类型一致性。
你的例子举错了。
Pair<Manager> managerPair = new Pair<>(CEO, CFO);
这行代码不能说明泛型的作用。

泛型的作用是jvm在执行时能直接强转。
比如List<String> list,在执行时能直接String a = list.get(0);不用担心强转出错。
因为类型擦除,list.get(0)返回的是Object,为什么能直接强转为String?因为编译器做了“安检”工作。

至于那行为什么报错,鼠标放在错误上idea就会告诉你为什么错了。类型不匹配。这也是编译器在泛型中起的作用。

  1. 关于类型擦除你理解的是对的,但运行时 JVM 不检查类型。
  2. 之所以报错是因为编译器在编译时会先检查泛型类型、再类型擦除的,而不是直接就类型擦除了,编译器又不傻。
  3. 你要非想像你写的那么干,需要用反射实现,因为反射是在运行时才执行的。

关于类型擦除我觉得就简单理解就行了,即在运行期间,不确定类型,安全完全由编译器保证。这导致出现了很多莫名其妙的bug,比如List list = new ArrayList<String>() list.add(123) 和一些本应该由泛型完成的事情,它完成不了。比如List<T> 无法在运行时获取其确切的类型,只能通过一些蹩脚的方式来获取,带来很大的不便,也不够优雅。java 的泛型 属于它的弱项,不过通过一些列的强制规范 和 补救措施 也可以用,只是需要多付出一些努力,就是在还债。

关于类型转换的问题,我觉得你最好把关系继承图放出来,不知道你啥继承关系,不太好说,总的来说就是向上强转时安全的,可以随意转换,但向下强转必须用户来,编译器不认。你的操作感觉时把编译器想得太傻了。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题