被Java泛型混淆需要强制转换

Eri*_*erg 3 java generics casting

我对以下代码感到困惑:

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class GenericsTest<T extends List> {

  public void foo() {
    T var = (T) new LinkedList();
  }

  public static void main(String[] args) {
      GenericsTest<ArrayList> gt1 = new GenericsTest<ArrayList>();
      gt1.foo();
      System.out.println("Done");
  }
}
Run Code Online (Sandbox Code Playgroud)

无论我传递给构造函数的Type参数如何,运行时类型T似乎都是java.util.List.

那么为什么编译器T在分配var时需要转换?它不应该在编译时知道LinkedList可分配给List吗?

我理解代码是假的,我理解为什么它在运行时工作,即使它看起来不应该.令我困惑的部分是为什么编译器要求我在进行赋值时键入(T)?然而,如果没有伪造的话,它可以很好地编译.

据推测,编译器理解擦除.看起来编译器应该能够在没有强制转换的情况下编译代码.

Jon*_*eet 11

LinkedList可分配给List,但可能无法分配给T.

在某些方面,您的测试代码不应该起作用 - 它只能起作用,因为编译器有效地删除了强制转换,因为它在执行时没用(由于类型擦除).它在编译时非常有用,因为它要求您(开发人员)向编译器承诺您正在执行无法检查的有效内容.碰巧,你正在做的事情无效的,但编译器无法知道这一点,并且没有更多信息在执行时无法检查它.您可以提供以下信息:

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class GenericsTest<T extends List> {

  Class<T> clazz;

  public GenericsTest(Class<T> clazz) {
    this.clazz = clazz;
  }

  public void foo() {
    T var = clazz.cast(new LinkedList());
  }

  public static void main(String[] args) {
    GenericsTest<ArrayList> gt1 = 
        new GenericsTest<ArrayList>(ArrayList.class);
    gt1.foo();
    System.out.println("Done");
  }
}
Run Code Online (Sandbox Code Playgroud)

现在这将失败并出现相应的异常 - 类型信息在执行时仍然可用,我们正在使用它以安全的方式执行转换.

请注意,如果使用适当的警告进行编译,编译器将告诉您原始代码实际上并未检查任何内容.

有关此主题和许多其他主题的更多信息,请查看相当全面的Java泛型常见问题解答.

  • 这并非毫无意义 - 它让开发人员承认这些类型不一定合适.添加演员后的警告告诉开发人员,演员只是一个真正的创可贴; 还有一个问题等着咬你. (2认同)

eri*_*son 7

海报在评论中问道,

但是,据推测,编译器知道这就是为什么需要将转换为(T).演员有没有可能失败的方式?

这个演员阵容不会失败.但是编译器警告说,这个代码设置一个定时炸弹滴答炸毁别处ClassCastException.

在该示例中,没有理由使用泛型,因为API都不使用类型变量T.看看更真实的泛型应用.

   public class GenericsTest<T extends List> {

 3   public T foo() {
 4     T var = (T) new LinkedList();
 5     return var;
 6   }

 8   public static void main(String... argv) {
 9     GenericsTest<ArrayList> gt1 = new GenericsTest<ArrayList>();
10     gt1.foo();
11     System.out.println("Test one okay");
12     ArrayList<?> list = gt1.foo();
13     System.out.println("Test two okay");
14   }

   }
Run Code Online (Sandbox Code Playgroud)

ClassCastException在第12行抛出A ClassCastException,没有演员阵容?调用代码完全正确.无效的强制转换(bug)位于调用方法的第4行.但是这个例外是在某个时间和地点提出的.

Java泛型的目的是确保代码是类型安全的.如果所有代码都是在没有"未选中"警告的情况下编译的,那么保证ClassCastException在运行时不会引发任何代码.但是,如果您所依赖的库写得不正确,就像这个例子一样,承诺就会被破坏.