美文网首页
泛型擦除引发的一个血案

泛型擦除引发的一个血案

作者: 福later | 来源:发表于2020-07-03 13:46 被阅读0次

先上代码
Node.java

public class Node<T> {
    public T data;
    public Node(T data) { this.data = data; }
    public void setData(T data) {
        System.out.println("Node.setData");
        this.data = data;
    }
}

MyNode.java

public class MyNode extends Node<Integer> {
    public MyNode(Integer data) { super(data); }
    public void setData(Integer data) {
        System.out.println("MyNode.setData");
        super.setData(data);
    }
}

Test.java

    public static void main(String[] args) {
        MyNode mn = new MyNode(5) ;
        Node n = mn; 
        n.setData("Hello"); 
 
    }

我们先想一下,执行Test.java 中的main方法会发生什么?哈哈,你们先想,我接着分析我的
想法,这几个类编译后应该是这样的
Node.java

public class Node {
    public Object data;
    public Node(Object  data) { this.data = data; }
    public void setData(Object  data) {
        System.out.println("Node.setData");
        this.data = data;
    }
}

MyNode.java

public class MyNode extends Node<Integer> {
    public MyNode(Integer data) { super(data); }
    public void setData(Integer data) {
        System.out.println("MyNode.setData");
        super.setData(data);
    }
}

Test.java

    public static void main(String[] args) {
        MyNode mn = new MyNode(5) ;
        Node n = mn; 
        n.setData("Hello"); 
    
    }

那么n.setData方法其实最终会调用到父类Node类中的setData(Object data)方法,所以最后会打印出Node.setData;.
好,分析完了,代码运行下,what?竟然报了一个String can not be cast to Integer 异常;咋回事啊。
其实代码很简单,结果却不能预期,这时候我们就要想想能否从字节码上看看,能不能找出端倪,因为源码上没有太多的信息了。
MyNode.class

// class version 51.0 (51)
// access flags 0x21
// signature Lnfc/ips/com/test/Node<Ljava/lang/Integer;>;
// declaration: nfc/ips/com/test/MyNode extends nfc.ips.com.test.Node<java.lang.Integer>
public class nfc/ips/com/test/MyNode extends nfc/ips/com/test/Node {

  // compiled from: MyNode.java

  // access flags 0x1
  public <init>(Ljava/lang/Integer;)V
   L0
    LINENUMBER 9 L0
    ALOAD 0
    ALOAD 1
    INVOKESPECIAL nfc/ips/com/test/Node.<init> (Ljava/lang/Object;)V
    RETURN
   L1
    LOCALVARIABLE this Lnfc/ips/com/test/MyNode; L0 L1 0
    LOCALVARIABLE data Ljava/lang/Integer; L0 L1 1
    MAXSTACK = 2
    MAXLOCALS = 2

  // access flags 0x1
  public setData(Ljava/lang/Integer;)V
   L0
    LINENUMBER 11 L0
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    LDC "MyNode.setData"
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
   L1
    LINENUMBER 12 L1
    ALOAD 0
    ALOAD 1
    INVOKESPECIAL nfc/ips/com/test/Node.setData (Ljava/lang/Object;)V
   L2
    LINENUMBER 13 L2
    RETURN
   L3
    LOCALVARIABLE this Lnfc/ips/com/test/MyNode; L0 L3 0
    LOCALVARIABLE data Ljava/lang/Integer; L0 L3 1
    MAXSTACK = 2
    MAXLOCALS = 2

  // access flags 0x1041
  public synthetic bridge setData(Ljava/lang/Object;)V
   L0
    LINENUMBER 8 L0
    ALOAD 0
    ALOAD 1
    CHECKCAST java/lang/Integer
    INVOKEVIRTUAL nfc/ips/com/test/MyNode.setData (Ljava/lang/Integer;)V
    RETURN
   L1
    LOCALVARIABLE this Lnfc/ips/com/test/MyNode; L0 L1 0
    MAXSTACK = 2
    MAXLOCALS = 2
}

我们会发现多了一个setData方法,这应该是是编译器生成的,CHECKCAST java/lang/Integer 这句话的翻译成java代码就是(Integer)Object,我们把 public synthetic bridge setData(Ljava/lang/Object;) 这个方法翻译下就变成了下面的了

  public void setData(Object data) {
      setData((Integer)data) ;
    }

原因我们就清楚了, n.setData("Hello")调用的是编译器生成的一个桥接的方法,如上,自然会报异常。

相关文章

  • 泛型擦除引发的一个血案

    先上代码Node.java MyNode.java Test.java 我们先想一下,执行Test.java 中的...

  • 【进阶之路】Java的类型擦除式泛型

    【进阶之路】Java的类型擦除式泛型 Java选择的泛型类型叫做类型擦除式泛型。什么是类型擦除式泛型呢?就是Jav...

  • 泛型

    泛型用于编译时期,确保类型的安全 在运行时,会将泛型去掉,class文件是不带泛型的,这个称为泛型的擦除,擦除是为...

  • Android 开发也要掌握的Java知识 - Java泛型

    如果需要看泛型擦除Java泛型擦除 1.Java泛型有什么用?为啥要使用泛型? Java中数组的类型是定义的时候就...

  • JAVA泛型和类型擦除

    什么是类型擦除 Java是使用擦除来实现泛型的。使用泛型后在运行时任何具体的类型信息都被擦除了,关于泛型的处理都是...

  • java泛型

    java的泛型是"伪泛型",为什么这么说。因为泛型只是作用在编译之前,编译之后,泛型都被擦除了(类型擦除)。所以说...

  • 泛型 & 注解 & Log4J日志组件

    掌握的知识 : 基本用法、泛型擦除、泛型类/泛型方法/泛型接口、泛型关键字、反射泛型(案例) 泛型 概述 : 泛型...

  • Java如何在运行时获取泛型的类型

    Java泛型是伪泛型,会在编译完成时进行类型的擦除,我们无法在运行时获取泛型参数的具体类型(类型擦除会被替换成泛型...

  • Kotlin语言(六):泛型

    1、泛型类 2、泛型函数 3、泛型上限 4、泛型擦除 5、泛型投射 6、星号投射

  • 15 泛型

    1.泛型类 2.泛型方法 3.擦除

网友评论

      本文标题:泛型擦除引发的一个血案

      本文链接:https://www.haomeiwen.com/subject/awbjqktx.html