我正在阅读有关C模板和C#泛型的讨论,以及它们与
Java类型擦除泛型的不同之处.我读了一条声明说Java仍然在运行时使用转换,例如在处理集合时.如果这是真的,我不知道它!
假设我有以下代码:
ArrayList<SomeClass> list = new ArrayList<SomeClass>(); ... SomeClass object = list.get(0);
我的问题是.这是否有效编译
ArrayList list = new ArrayList(); ... SomeClass object = (SomeClass) list.get(0);
如果是这样,为什么?我认为列表的类型是ArrayList< SomeClass>在编译时和运行时保证只有SomeClass存储在ArrayList中?或者你可以做不安全的类型转换来转换ArrayList< OtherClass>到ArrayList< SomeClass>?
是否有其他场合在Java泛型中进行运行时类型转换?
最后,如果确实使用了在运行时进行的转换,那么JIT是否可以忽略运行时转换检查?
(请不要回答/评论微观优化是不值得的,先发制人的优化是万恶之源等等.我在其他类似的问题上看到了这些.这些点很好理解,但它们并没有带走尝试点了解如何在引擎盖下实现类型擦除的泛型.)
解决方法
这是我写的一个简短的程序:
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); } }
尝试用javac编译它,然后用javap -verbose查看字节码.我选择了几条有趣的线条:
public java.lang.Object contents;
这应该在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
然后我们可以在第17行看到checkcast命令,就在println之前 – 这是Java从Object转换为擦除的泛型类型的字符串 – String