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

时间:2009-04-23 20:57:14

标签: 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");
  }
}

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

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

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

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

4 个答案:

答案 0 :(得分: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");
  }
}

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

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

有关此主题和许多其他主题的更多信息,请查看非常全面的Java Generics FAQ

答案 1 :(得分: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   }

   }

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

Java泛型的目的是确保代码是类型安全的。如果代码的所有编译时没有“未选中”警告,则保证在运行时不会引发ClassCastException。但是,如果你所依赖的库写得不正确,就像这个例子那样,承诺就会被破坏。

答案 2 :(得分:1)

那是因为type erasure;在编译时,泛型参数变为其下限。

编译器试图告诉你出了问题。如果它按照您认为应该执行的操作,那么您将获得ClassCastException - 您无法将LinkedList投射到ArrayList

答案 3 :(得分:0)

我以为你是来自C#?在这种情况下,Java泛型不是C#泛型。

当您提供泛型类型参数时,Java不生成强类型类,它只使用最小公用命名类型,该类型可用于可能提供的所有可能的泛型类型参数组合。

因此,在这种情况下,您使用List,因为您指定了extends List