Java泛型何时在运行时使用转换?

Max*_*Max 6 java generics

我正在阅读有关C++模板和C#泛型的讨论,以及它们与Java类型擦除泛型的不同之处.我读了一条声明说Java仍然在运行时使用转换,例如在处理集合时.如果这是真的,我不知道它!

假设我有以下代码:

ArrayList<SomeClass> list = new ArrayList<SomeClass>();
...
SomeClass object = list.get(0);
Run Code Online (Sandbox Code Playgroud)

我的问题是.这是否有效编译

ArrayList list = new ArrayList();
...
SomeClass object = (SomeClass) list.get(0);
Run Code Online (Sandbox Code Playgroud)

如果是这样,为什么?我认为list是类型ArrayList<SomeClass>保证的事实,在编译时和运行时,只有SomeClass将存储在ArrayList中?或者你可以做不安全的类型转换来转换ArrayList<OtherClass>ArrayList<SomeClass>

是否有其他场合在Java泛型中进行运行时类型转换?

最后,如果确实使用了在运行时进行的转换,那么JIT是否可以忽略运行时转换检查?

(请不要回答/评论微观优化是不值得的,先发制人的优化是万恶之源等等.我在其他类似的问题上看到了这些.这些点很好理解,但它们并没有带走尝试点了解如何在引擎盖下实现类型擦除的泛型.)

SLa*_*aks 5

你的假设是正确的.

始终需要进行类型检查,因为您可以编写以下法律代码:

ArrayList<X> good = new ArrayList<X>();
ArrayList q = x;
ArrayList<Y> evil = (ArrayList<Y>)q;  //Doesn't throw due to type erasure
evil.add(new Y()); //this will actually succeed
X boom = good.get(0);
Run Code Online (Sandbox Code Playgroud)

转换为ArrayListto ArrayList<Y>将(总是)给出一个未经检查的强制转换警告,但是(也总是)会在运行时成功.


Ric*_*nte 5

这是我写的一个简短的程序:

public class Test<T> {

  public T contents;

  public Test(T item) {
    contents = item;
  }

  public static void main(String[] args) {

    Test<String> t = new Test<String>("hello");
    System.out.println(t.contents);

  }

}
Run Code Online (Sandbox Code Playgroud)

尝试用它编译javac,然后查看字节码javap -verbose.我选择了几条有趣的线条:

public java.lang.Object contents;
Run Code Online (Sandbox Code Playgroud)

这应该在Test构造函数的定义之前出现.在示例代码中,它是T类型,现在它是一个Object.这是擦除.

现在,看一下主要方法:

public static void main(java.lang.String[]);
Code:
   Stack=3, Locals=2, Args_size=1
   0:   new #3; //class Test
   3:   dup
   4:   ldc #4; //String hello
   6:   invokespecial   #5; //Method "<init>":(Ljava/lang/Object;)V
   9:   astore_1
   10:  getstatic   #6; //Field java/lang/System.out:Ljava/io/PrintStream;
   13:  aload_1
   14:  getfield    #2; //Field contents:Ljava/lang/Object;
   17:  checkcast   #7; //class java/lang/String
   20:  invokevirtual   #8; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   23:  return
Run Code Online (Sandbox Code Playgroud)

然后我们可以checkcast在第17行看到命令,就在println之前 - 这是Java从Object被删除的泛型类型转换的地方-String