Java泛型何时在运行时使用强制转换? 我正在阅读一个关于C++模板和C泛型的讨论,以及它们与java类型的泛型的区别。我读到一条声明说Java仍然在运行时使用强制转换,例如在处理集合时。如果这是真的,我没有意识到

Java泛型何时在运行时使用强制转换? 我正在阅读一个关于C++模板和C泛型的讨论,以及它们与java类型的泛型的区别。我读到一条声明说Java仍然在运行时使用强制转换,例如在处理集合时。如果这是真的,我没有意识到,java,generics,Java,Generics,假设我有如下代码: ArrayList<SomeClass> list = new ArrayList<SomeClass>(); ... SomeClass object = list.get(0); 若然,原因为何?我认为列表的类型是ArrayList这一事实保证了在编译时和运行时,ArrayList中只存储某个类?或者,您是否可以执行不安全的类型转换来将ArrayList转换为ArrayList 是否有其他情况下在运行时类型转换是在Java泛型中完成的 最后,如果

假设我有如下代码:

ArrayList<SomeClass> list = new ArrayList<SomeClass>();
...
SomeClass object = list.get(0);
若然,原因为何?我认为列表的类型是
ArrayList
这一事实保证了在编译时和运行时,ArrayList中只存储某个类?或者,您是否可以执行不安全的类型转换来将
ArrayList
转换为
ArrayList

是否有其他情况下在运行时类型转换是在Java泛型中完成的

最后,如果确实使用了运行时强制转换,那么JIT是否可以省略运行时强制转换检查


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

类型检查始终是必要的,因为您可以编写以下法律代码:

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);
ArrayList good=new ArrayList();
阵列列表q=x;
ArrayList邪恶=(ArrayList)q//由于类型擦除而不会抛出
添加(新的Y())//这实际上会成功
X动臂=良好。获取(0);

ArrayList
ArrayList
的强制转换将(始终)发出未经检查的强制转换警告,但(也始终)会在运行时成功。

请记住,在运行时,
ArrayList
ArrayList
相同,并使用底层的
对象[]
实现。JIT通常非常聪明,可以省略强制转换,但是由于泛型在运行时被删除,强制转换是必要的。

Java在保持版本之间的兼容性方面做得很好,这意味着Java5+语法可以编译成1.4兼容的类。我想这就是为什么ArrayList在运行时不保证只在ArrayList中存储某个类的部分原因

要了解泛型代码真正编译成什么,请使用这个优秀的工具


您的代码确实编译成
SomeClass object=(SomeClass)list.get(0)

以下是我编写的一个简短程序:

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);

  }

}
这应该在定义测试构造函数之前出现。在示例代码中,它的类型是T,现在它是一个对象。那是擦除

现在,看看主要的方法:

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
publicstaticvoidmain(java.lang.String[]);
代码:
堆栈=3,局部变量=2,参数大小=1
0:新#3//课堂测试
3:dup
4:最不发达国家4//串你好
6:特别是#5//方法“”:(Ljava/lang/Object;)V
9:astore_1
10:getstatic#6//字段java/lang/System.out:Ljava/io/PrintStream;
13:aload_1
14:getfield#2//字段内容:Ljava/lang/Object;
17:checkcast#7//类java/lang/String
20:invokevirtual#8//方法java/io/PrintStream.println:(Ljava/lang/String;)V
23:返回
然后我们可以在第17行看到
checkcast
命令,就在println之前-这是Java从
Object
转换到已擦除的泛型类型-
String

我认为列表是ArrayList类型的事实保证了 编译时和运行时,其中只存储某个类 ArrayList?或者,您是否可以执行不安全的类型转换来转换 将ArrayList转换为ArrayList

是,类型信息在运行时被丢弃

这意味着您无法理解是具有类型为
ArrayList
的对象还是类型为
ArrayList
的对象。JVM不知道泛型类实例的实际类型参数,因此您没有运行时检查(只有在使用该数据时才会看到错误)

此代码有效:

public void enqueueItem(ArrayList<Integer> list, Integer item) {
    List listAlias = list;
    listAlias.add(x.toString());
}
是否有其他情况下在运行时类型转换是在Java泛型中完成的

是的,每次您使用它们时(如果您对泛型做了一些不好的事情,您就会在这一点上出错)

最后,如果确实使用了运行时强制转换,那么JIT是否可以省略运行时强制转换检查

我认为不会,它总是执行强制转换,但这是一个非常详细的实现细节,不管怎样,它跟踪子类的类型参数,因此如果您从泛型类派生自己的类,那么您将从这个问题中得到解决

若然,原因为何


所有现有代码都将与新的泛型一起工作,即使您更改了一些接口。这意味着您可以逐步更新到泛型。我不担心对性能的影响(我们用它生存了很长一段时间,现在这是一个问题吗?),但我不喜欢所有这些技巧。也许,毕竟,我们不能让橡树满了,妻子也喝了……

你有很多问题。但答案可以归结为:Java泛型的出现是为了改进编译时错误检查。Oracle(Sun)提供的各种泛型信息和教程几乎涵盖了这一点。为了保持向后兼容性,泛型作为编译时特性被附加。斯莱克斯、里坎特和阿德里亚诺的回答都在向我解释。底线是@user1291492提到的,泛型主要用于编译时错误检查。现在仍然有点不清楚JIT是做什么的。在我看来,如果Java的语义不允许不安全的转换(例如
ArrayList
ArrayList
),那么您仍然可以优化运行时检查,但这是另一个问题的主题。我认为Richante的回答是“正确的”,因为编译和反汇编所付出的努力(谢谢!)@Max,仅仅因为Java字节码包含cas
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
public void enqueueItem(ArrayList<Integer> list, Integer item) {
    List listAlias = list;
    listAlias.add(x.toString());
}
public ArrayList getCollection() {
   return new ArrayList<Integer>();
}

public void doStuff() {
   ArrayList<String> list = getCollection();
   list.add("text");
}