带有List 方法的Java通用接口上的编译器错误

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

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

似乎接口上的类型擦除导致编译器忘记第二个方法上的声明类型,这与generics类型无关。 或者换句话说,为什么接口上的generics类型会影响编译器如何理解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 values = task.getIDs(); // getting a compiler error for this line for (Integer value : task.getIDs()){ } } } interface Task{ U doSomething(String value); List getIDs(); } 

接口的实现没有必要certificate这一点,但我不想留下引用Task task = null; 并有答案告诉我这是问题所在。

 class MyTask implements Task{ @Override public Boolean doSomething(String value) { System.out.println(value); return false; } @Override public List getIDs() { return Arrays.asList( 1, 2, 3, 4 ); } } 

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

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

编辑:

JLS-4.8-210在提及时提到了这一点(信用: zhong-j-yu ):

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

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

错误似乎在于:

 Task task = new MyTask(); 

您忘记在Task后添加generics。 它应该工作,如果您将其更改为以下之一:

 Task task = new MyTask(); Task task = new MyTask(); 

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

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

我相信这说明如果你声明一个使用generics参数( Task )声明的类型( Task )而没有所述generics参数,它的所有函数也会丢失它们的generics类型,无论它们是否相关。 因此,编译器将task.getIDs()解释为返回普通List ,而不是List 。 当然,它的迭代器会生成Objects而不是Integers ,从而导致您看到的编译器错误。

这样做的原因可能是向后兼容Java 1.5之前生成的代码,当时引入了generics。