Java泛型和返回类型

9

我遇到了一些不理解的问题。为什么下面的 for each 循环是非法的,而第二个完全相同的循环却可以?

public interface SomeInterface<T> {
   List<SomeNamedObject> getObjects();
   void doSomething(P1 p1, T p2);
}

public class SomeNamedObject {
    private String text;
}

public class Clazz {

    private SomeInterface someInterface;

    ...

    public void someMethod() {
       // Error Type mismatch: cannot convert from element type Object to TestClass.SomeNamedObject
       for (SomeNamedObject someNamedObject :  someInterface.getObjects()) {
             // This loop won't compile as the someInterface.getObjects returns just a List and not a List<SomeNamedObject>
       }

       // Warning Type safety: The expression of type List needs unchecked 
       // conversion to conform to List<TestClass.SomeNamedObject>
       List<SomeNamedObject> objects = someInterface.getObjects();
       for (SomeNamedObject someNamedObject :  objects) {
             // This loop compiles 
       }
    }
}

不是一个 bug,而是与擦除和原始类型有关的问题。 - Stefan
我不知道问题可能是什么。你能发布实际的堆栈跟踪(只需前几行)吗?我猜你的完整类需要“SomeInterface <T>”,但示例不需要“<T>”。也许里面有什么东西? - Jay
1
@Jay声明了一个someInterface,但没有指定泛型类型,Java则会退而使用原始类型,并将方法签名更改为返回一个原始List(请参见答案)。然而他应该在对象赋值时提及警告。 - Stefan
2个回答

18
因为您的实例变量 private SomeInterface someInterface 没有指定其泛型类型参数,因此禁用了对 someInterface 的所有泛型使用。这意味着 someInterface.getObjects() 具有原始返回类型 List 而不是 List<SomeNamedObject>。 这就是第一个示例无法编译的原因。
在第二个示例中,List<SomeNamedObject> objects = someInterface.getObjects() 为列表放入了显式类型。然而,当您这样做时,您会看到一个警告,因为类型安全性不能保证。如果没有类型参数定义为 List getObjects(),则会出现相同的行为。

可能原始文件中有个打字错误,但 SomeInterface<T> 并没有使用 T 来指定返回的 List 的泛型类型,而是硬编码为 SomeNamedObject。因此,即使实例变量没有指定类型,也不会影响返回的 List - Matt D
5
如果在 someInterface 的声明中未指定通用参数,则即使它们不依赖于 T,也将忽略/禁用 SomeInterface 中的所有通用类型。 - mikej
非常有趣,尽管有些奇怪。 - Matt D

3

请注意,当您在第二个循环之前分配对象时,编译器会发出警告。

Type safety: The expression of type List needs unchecked conversion to conform to 
 List<TestClass.SomeNamedObject>

这表明您的getObjects()方法返回了一个非泛型List,这解释了为什么第一个循环不编译。因为您忘记了将引用加上泛型:
private SomeInterface someInterface;

如果你不将其泛型化,那么所有东西都将使用原始类型,包括声明方法的签名。这意味着它返回一个原始的List对象而不是一个List<SomeNamedObject>。 可以像下面这样做:

private SomeInterface<Object> someInterface;

它应该可以工作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接