14

我不明白以下代码导致的编译器错误。我定义了一个通用接口,请参阅任务,它有两种方法:U doSomething(String value)List<Integer> getIDs(). doSomething() 方法实际上使用泛型类型作为其返回值的类型,但似乎没有引起问题。该getIDs()方法返回一个List,它与Task的类型无关,但是在使用for..each语句迭代返回值时会导致问题。发生以下编译器错误。

error: incompatible types
    for (Integer value : task.getIDs()){
required: Integer
found:    Object

似乎接口上的类型擦除导致编译器忘记了第二种方法上声明的类型,这与泛型类型无关。或者换句话说,为什么接口上的泛型类型会影响编译器如何理解getIDs()方法的返回值,特别是在 for..each 语句的上下文中?

显然,如果我得到对 for..each 之外的列表的引用,则没有问题,但不是直接的。

public class InterfaceTest {
   public static void main(String[] args) {
      Task task = new MyTask();
      // no complaints about the type here     
      List<Integer> values = task.getIDs();

      // getting a compiler error for this line
      for (Integer value : task.getIDs()){

      }
   }
}


interface Task<U>{
   U doSomething(String value);
   List<Integer> getIDs();
}

接口的实现不需要说明这一点,但我不想留下参考Task task = null;并有答案告诉我这就是问题所在。

class MyTask implements Task<Boolean>{

   @Override
   public Boolean doSomething(String value) {
      System.out.println(value);
      return false;
   }

   @Override
   public List<Integer> getIDs() {
      return Arrays.asList( 1, 2, 3, 4 );
   }
}
4

3 回答 3

9

发生的情况是,当使用带有泛型参数的类(或接口)<T>但引用没有<T>(即该raw类型)的实例时,编译器会从类中删除所有泛型类型信息。这可能是由于与 1.5 之前的源代码兼容,您根本无法使用泛型类型信息。

考虑您在 Java 1.4 编译器上编写代码和编译的情况。您想使用一个使用泛型的库。当您将该库中具有泛型参数的类型作为原始类型引用时,编译器会强制不使用泛型参数。

编辑:

JLS-4.8-210在提到时暗示了这一点(来源:zhong-j-yu):

未从其超类或超接口继承的原始类型 C 的构造函数(第 8.8 节)、实例方法(第 8.4 节、第 9.4 节)或非静态字段(第 8.3 节)的类型是对应的原始类型在对应于 C 的泛型声明中擦除其类型。

这仍然感觉像是一个陷阱,但可能出于某种原因。

于 2013-06-19T02:29:05.933 回答
4

错误似乎在这里:

Task task = new MyTask();

您忘记在Task. 如果您将其更改为以下之一,它应该可以工作:

Task<Boolean> task = new MyTask();
Task<?> task = new MyTask();
于 2013-06-19T02:23:31.197 回答
4

如果我正确解释了Java 语言规范(第 4.6 节。类型擦除),这是该语言的“陷阱”:

类型擦除还将构造函数或方法的签名(第 8.4.2 节)映射到没有参数化类型或类型变量的签名。构造函数或方法签名 s 的擦除是由与 s 相同的名称和 s 中给出的所有形式参数类型的擦除组成的签名。

我相信这表明,如果您声明一个类型 ( Task),该类型 () 是用泛型参数 ( Task<U>) 声明的,而没有所述泛型参数,那么它的所有函数也会失去它们的泛型类型,无论它们是否相关。因此,task.getIDs()编译器会将您解释为返回一个 plain List,而不是一个List<Integer>. 当然,迭代器会产生Objects, not Integers,从而导致您看到的编译器错误。

其原因可能是与 Java 1.5 之前生成的代码向后兼容,当时引入了泛型。

于 2013-06-19T02:43:58.360 回答