Java Generics的类型擦除会导致完全类型转换吗?

Jam*_* IV 12 java generics performance

我知道当Java编译器编译泛型类型时,它会执行Type erasure并从代码中删除对泛型的所有引用,而我ArrayList<Cheesecake>只是一个ArrayList.

我没有明确答案的问题是,是否缺少类型(因此强制类型转换)会导致速度减慢.换句话说,如果我使用标准的Java编译器和Oracle的标准JVM 1.7:

  • 字节码是否包含类型转换?
  • 如果是,是否包括运行时检查以检查其类型是否正确?
  • 转换本身是否需要非常重要的时间?
  • 将我自己的CheesecakeList类看起来相同ArrayList,只是所有的Objects转向Cheesecakes我会获得任何东西(再次只是一个假设,我不感兴趣因为更多的类而没有重复的二进制文件的副作用我的代码).

我对任何其他类型擦除造成的问题都不感兴趣,只是一个比我更了解JVM的人的简单回答.

我对这个例子中的成本最感兴趣:

List<Cheesecake> list = new ArrayList<Cheesecake>();
for (int i = 0; i < list.size(); i++)
{
   // I know this gets converted to ((Cheesecake)list.get(i)).at(me)
   list.get(i).eat(me); 
}
Run Code Online (Sandbox Code Playgroud)

与以下相比,在循环内转换是昂贵和/或重要的:

CheesecakeList list = new CheesecakeList();
for (int i = 0; i < list.size(); i++)
{
   //where the return of list.get() is a Cheesecake, therefore there is no casting.
   list.get(i).eat(me); 
}
Run Code Online (Sandbox Code Playgroud)

免责声明:这主要是学术好奇的问题.我真的怀疑类型转换有任何重大的性能问题,如果我在代码中发现性能错误,那么删除对它们的需求甚至不会是我要做的前五件事之一.如果你正在阅读这篇文章是因为你有性能问题请自己帮个忙,然后启动一个分析器并找出瓶颈的位置.如果你真的相信它的类型转换,那么你应该尝试以某种方式优化它.

Ita*_*man 15

短篇小说:是的,有一种类型检查.这是证明 -

鉴于以下课程:

// Let's define a generic class.
public class Cell<T> {
  public void set(T t) { this.t = t; }
  public T get() { return t; }

  private T t;
}

public class A {

  static Cell<String> cell = new Cell<String>();  // Instantiate it.

  public static void main(String[] args) {
    // Now, let's use it.
    cell.set("a");
    String s = cell.get(); 
    System.out.println(s);
  }  
}
Run Code Online (Sandbox Code Playgroud)

A.main()编译成(反编译通过javap -c A.class)的字节码如下:

public static void main(java.lang.String[]);
Code:
   0: getstatic     #20                 // Field cell:Lp2/Cell;
   3: ldc           #22                 // String a
   5: invokevirtual #24                 // Method p2/Cell.set:(Ljava/lang/Object;)V
   8: getstatic     #20                 // Field cell:Lp2/Cell;
  11: invokevirtual #30                 // Method p2/Cell.get:()Ljava/lang/Object;
  14: checkcast     #34                 // class java/lang/String
  17: astore_1      
  18: getstatic     #36                 // Field java/lang/System.out:Ljava/io/PrintStream;
  21: aload_1       
  22: invokevirtual #42                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
  25: return        
}
Run Code Online (Sandbox Code Playgroud)

正如你可以在偏移量中14,结果cell.get()是类型检查以验证它确实是一个字符串:

 14: checkcast     #34                 // class java/lang/String
Run Code Online (Sandbox Code Playgroud)

这确实会导致一些运行时惩罚.但是,由于JVM已经过很好的优化,因此其效果可能很小.

更长的故事:

你会如何实施这样的CheesecakeList课程?这个类不会定义一个数组来保存元素吗?请注意,对阵列的每个任务都会产生隐藏的类型检查.所以你不会获得你想象的那么多(尽管你的程序可能会执行比写操作更多的读操作,因此Cheesecak[]数组会给你一些东西).

底线:不要过早优化.

最后评论.人们通常认为类型擦除意味着Cell<String>编译成Cell<Object>.这不是真的.擦除仅适用于泛型类/方法的定义.它不适用于这些类/方法的使用地点.

换句话说,该类Cell<T>被编译为好像它被编写为Cell<Object>.如果T有一个uppper绑定(比方说Number)然后它编译成Cell<Number>.在代码中的其他位置,通常是类型是Cell类的实例化的变量/参数(例如Cell<String> myCell),不应用擦除.myCell类型的事实Cell<string>保存在类文件中.这允许编译器正确地检查程序.

  • *"底线:不要过早优化."*这个.+1 (4认同)