带有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。