Java Generics - 原始类型和参数化类型转换

时间:2016-11-29 16:07:45

标签: java generics raw-types

我一直在阅读Java Generics Tutorials和Stackoverflow中很少涉及泛型的线程,但仍然无法理解具体案例。 这是:

public class Box<T>
{
    private T t;

    public T getT ()
    {
        return t;
    }

    public void setT (T t)
    {
        this.t = t;
    }

    public static void main (String[] args)
    {
        Box<Integer> intBox = new Box<Integer>();
        Box rawBox = intBox;
        rawBox.setT("NBA");
        System.out.println(rawBox.getT());
        System.out.println(intBox.getT());
        /*1*/ //System.out.println(intBox.getT().toString());
    }
}

这是交易,我理解的第一个印刷品,即

 System.out.println(rawBox.getT());

打印NBA,因为rawBox是原始类型的Box T,它“给”我们对象。

我没有得到的是第二次印刷:

System.out.println(intBox.getT());

打印NBA。 intBox是泛型类型(在本例中为Box of Integers),这意味着它的getter方法应该返回一个类型为T的值(在本例中为Integer),所以我理解的是那里保存的String对象应该被转换to Integer(因为这是给Box T的参数类型),并且应该在运行时引发ClassCastException,但它不会发生,为什么会这样?

顺便说一句,注释号/ 1 /会增加混乱,因为如果我要取消注释它,它会导致在ruuntime中引发ClassCastException(String不能转换为Integer) ,我不明白

全部谢谢:)

3 个答案:

答案 0 :(得分:2)

有时很难猜出Java会在哪里插入已检查的强制转换。通常,它只会在必要时插入它们。理解你看到的行为的最好方法是检查字节码!

如果我们运行javap -c Box.class(在使用/*1*/未注释进行编译之后),我们会看到:

  public static void main(java.lang.String[]);
    Code:
      ...
      20: invokevirtual #8                  // Method getT:()Ljava/lang/Object;
      23: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      26: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
      29: aload_1
      30: invokevirtual #8                  // Method getT:()Ljava/lang/Object;
      33: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      36: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
      39: aload_1
      40: invokevirtual #8                  // Method getT:()Ljava/lang/Object;
      43: checkcast     #10                 // class java/lang/Integer
      46: invokevirtual #11                 // Method java/lang/Integer.toString:()Ljava/lang/String;
      49: invokevirtual #12                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      52: return

你可以在这里看到,JVM实际需要转换为整数的唯一时间是#43(checkcast)。这样就可以invokevirtual Integer.toString()

println(#33)的调用不需要演员,因为println需要Object,而不是Integer(也许您以为是在调用{{} 1}},但你不是)。所以JVM永远不需要检查它是否为整数,因为它不需要。

如果您调用了接受println(int)的方法,而不是致电println(Object),则应该看到Integer

例如,这个:

ClassCastException

将表演演员:

    ...
    print(intBox.getT());
}

private static void print(Integer integer) {
    System.out.println(integer);
}

答案 1 :(得分:1)

类型擦除是原因。在运行时,所有Box实例都相同,它们都包含引用。泛型只对程序员有益,可以传达类型信息并保持您的程序输入良好。

答案 2 :(得分:0)

这就是编译器警告您不要使用非泛型类引用的原因。在运行时,它无法知道intbox的类型,因为泛型不会保存在字节码中。