使用列表方法在Java泛型接口上编译错误

时间:2022-03-19 01:11:36

I don't understand the compiler error resulting from the following code. I define a generic interface, see Task, with two methods: U doSomething(String value) and List<Integer> getIDs(). The doSomething() method actually uses the generic type as the type of its return value, but doesn't seem to be causing problems. The getIDs() method returns a List, which is unrelated to the type of Task, but it is causing problems when using for..each statement to iterate over the return value. The following compiler error occurs.

我不理解以下代码导致的编译错误。我定义了一个通用接口,请参见Task,其中有两个方法:U doSomething(String value)和List getIDs()。doSomething()方法实际上使用泛型类型作为返回值的类型,但似乎没有造成问题。getIDs()方法返回一个列表,该列表与任务的类型无关,但在使用for.. .时会导致问题。遍历返回值的每个语句。发生以下编译错误。

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

It seems that the type erasure on the interface is causing the compiler to forget the declared type on the second method, which is unrelated to the generic type. Or in other words why is the generic type on the interface affecting how the compiler understands the return value on the getIDs() method and specifically in the context of a for..each statement?

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

Apparently if I get reference to the list outside of the for..each there is no problem, but not directly.

很明显,如果我得到了列表之外的。每个都没有问题,但不是直接的。

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();
}

The implementation of the interface isn't necessary to demonstrate the point, but I didn't want to leave the reference Task task = null; and have answer's telling me that's the problem.

接口的实现并不需要说明这一点,但是我不想让reference 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 );
   }
}

3 个解决方案

#1


7  

What is happening is when use use a class (or interface) with a generic parameter <T> but refer to and instance of the without <T> (ie. that raw type) the compiler erases all generic type information from the class. This is likely due to compatibility with pre-1.5 source code where you wouldn't be able to use generic type information at all.

所发生的情况是,当使用具有泛型参数 的类(或接口),但引用和实例时,不使用 (即。这个原始类型)编译器从类中删除所有泛型类型信息。这可能是由于与1.5之前的源代码的兼容性,在这些源代码中,您根本无法使用泛型类型信息。

Consider the situation where you are writing code and compiling on a Java 1.4 compiler. You want to use a library which makes use of generics. When you refer to a type from that library which has generic parameters as a raw type, the compiler enforces the use of no generic parameters.

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

EDIT:

编辑:

The JLS-4.8-210 alludes to this when it mentions (credit: zhong-j-yu):

JLS-4.8-210在提到这一点时也提到了这一点(注:zhong-j-yu):

The type of a constructor (§8.8), instance method (§8.4, §9.4), or non-static field (§8.3) M of a raw type C that is not inherited from its superclasses or superinterfaces is the raw type that corresponds to the erasure of its type in the generic declaration corresponding to C.

构造函数的类型(§8.8),实例方法(§8.4,§9.4),或非静态字段(§8.3)米原始类型的C不是继承自父类或超接口是原始类型对应于其类型擦除的通用声明相应的C。

This still feels like a gotcha, but it is likely for some reason.

这仍然让人感觉像是一个陷阱,但出于某种原因,它很可能是存在的。

#2


3  

The error seems to lie here:

错误似乎在这里:

Task task = new MyTask();

You have forgotten to add generics after Task. It should work if you change it to one of these:

您已经忘记在任务之后添加泛型。如果你把它改成以下其中之一,它应该是有效的:

Task<Boolean> task = new MyTask();
Task<?> task = new MyTask();

#3


3  

If I am interpreting the Java Language Specification (§4.6. Type Erasure) correctly, this is a "gotcha" of the language:

如果我解释Java语言规范(§4.6。正确地说,这是语言的“陷阱”:

Type erasure also maps the signature (§8.4.2) of a constructor or method to a signature that has no parameterized types or type variables. The erasure of a constructor or method signature s is a signature consisting of the same name as s and the erasures of all the formal parameter types given in s.

类型擦除地图签名(§8.4.2)构造函数或方法的签名没有参数化的类型或类型的变量。构造函数或方法签名s的删除是一个签名,它由与s同名的签名和s中给出的所有形式参数类型的删除组成。

I believe that this states that if you declare a type (Task) that is declared with a generic parameter (Task<U>) without said generic parameter, all its functions also lose their generic types, whether they are related or not. Therefore, your task.getIDs() is interpreted by the compiler as returning a plain List, not a List<Integer>. The iterator for that, of course, produces Objects, not Integers, causing the compiler error you see.

我认为,如果您声明了一个类型(任务),它是用一个泛型参数(Task)声明的,而没有使用泛型参数,那么它的所有函数也会丢失它们的泛型类型,不管它们是否相关。因此,编译器将task.getIDs()解释为返回纯列表,而不是列表 。当然,迭代器会生成对象,而不是整数,这会导致您看到的编译器错误。

The reason for this is likely backwards compatibility with code produced before Java 1.5, when generics were introduced.

原因很可能是在引入泛型之前与Java 1.5之前生成的代码向后兼容。

#1


7  

What is happening is when use use a class (or interface) with a generic parameter <T> but refer to and instance of the without <T> (ie. that raw type) the compiler erases all generic type information from the class. This is likely due to compatibility with pre-1.5 source code where you wouldn't be able to use generic type information at all.

所发生的情况是,当使用具有泛型参数 的类(或接口),但引用和实例时,不使用 (即。这个原始类型)编译器从类中删除所有泛型类型信息。这可能是由于与1.5之前的源代码的兼容性,在这些源代码中,您根本无法使用泛型类型信息。

Consider the situation where you are writing code and compiling on a Java 1.4 compiler. You want to use a library which makes use of generics. When you refer to a type from that library which has generic parameters as a raw type, the compiler enforces the use of no generic parameters.

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

EDIT:

编辑:

The JLS-4.8-210 alludes to this when it mentions (credit: zhong-j-yu):

JLS-4.8-210在提到这一点时也提到了这一点(注:zhong-j-yu):

The type of a constructor (§8.8), instance method (§8.4, §9.4), or non-static field (§8.3) M of a raw type C that is not inherited from its superclasses or superinterfaces is the raw type that corresponds to the erasure of its type in the generic declaration corresponding to C.

构造函数的类型(§8.8),实例方法(§8.4,§9.4),或非静态字段(§8.3)米原始类型的C不是继承自父类或超接口是原始类型对应于其类型擦除的通用声明相应的C。

This still feels like a gotcha, but it is likely for some reason.

这仍然让人感觉像是一个陷阱,但出于某种原因,它很可能是存在的。

#2


3  

The error seems to lie here:

错误似乎在这里:

Task task = new MyTask();

You have forgotten to add generics after Task. It should work if you change it to one of these:

您已经忘记在任务之后添加泛型。如果你把它改成以下其中之一,它应该是有效的:

Task<Boolean> task = new MyTask();
Task<?> task = new MyTask();

#3


3  

If I am interpreting the Java Language Specification (§4.6. Type Erasure) correctly, this is a "gotcha" of the language:

如果我解释Java语言规范(§4.6。正确地说,这是语言的“陷阱”:

Type erasure also maps the signature (§8.4.2) of a constructor or method to a signature that has no parameterized types or type variables. The erasure of a constructor or method signature s is a signature consisting of the same name as s and the erasures of all the formal parameter types given in s.

类型擦除地图签名(§8.4.2)构造函数或方法的签名没有参数化的类型或类型的变量。构造函数或方法签名s的删除是一个签名,它由与s同名的签名和s中给出的所有形式参数类型的删除组成。

I believe that this states that if you declare a type (Task) that is declared with a generic parameter (Task<U>) without said generic parameter, all its functions also lose their generic types, whether they are related or not. Therefore, your task.getIDs() is interpreted by the compiler as returning a plain List, not a List<Integer>. The iterator for that, of course, produces Objects, not Integers, causing the compiler error you see.

我认为,如果您声明了一个类型(任务),它是用一个泛型参数(Task)声明的,而没有使用泛型参数,那么它的所有函数也会丢失它们的泛型类型,不管它们是否相关。因此,编译器将task.getIDs()解释为返回纯列表,而不是列表 。当然,迭代器会生成对象,而不是整数,这会导致您看到的编译器错误。

The reason for this is likely backwards compatibility with code produced before Java 1.5, when generics were introduced.

原因很可能是在引入泛型之前与Java 1.5之前生成的代码向后兼容。