一个List然后为什么代码(下面)编译?当然MyClass2
应该返回List<Integer>
?
public class Main {
public static void main(final String[] args) {
MyClass myClass = new MyClass();
List list = myClass.getList();
System.out.println(list);
System.out.println(list.get(0).getClass());
MyClass2 myClass2 = new MyClass2();
List list2 = myClass2.getList();
System.out.println(list2);
System.out.println(list2.get(0).getClass());
}
public interface Int1 {
public List getList();
}
public interface Int2 extends Int1 {
@Override
public List<Integer> getList();
}
public static class MyClass implements Int2 {
@Override
public List<Integer> getList() {
return Arrays.asList(1, 2, 3);
}
}
public static class MyClass2 implements Int2 {
@Override
public List getList() {
return Arrays.asList("One", "Two", "Three");
}
}
}
我注意到如果你试图使它成为List<String>
,那么你会得到一个错误“java:Main.MyClass2不是抽象的,并且不会覆盖Main.Int2中的抽象方法getList()”。我不太明白为什么你在上面的例子中没有得到这个。
注意:我的项目中的问题的解决方案是使接口本身通用,即Int1<X>
(当然我使用比这更好的名称,它只是一个例子)。
答案 0 :(得分:7)
我怀疑,编译器应该警告未经检查的转换,而不是将其标记为编译器错误。让我们理解为什么它会警告你:
您需要执行以下两种方法来满足您实现的接口合同:
public List getList();
public List<Integer> getList();
现在,如果在您的类中,您只提供第一个方法的实现,它可以处理2 nd 方法的请求。您可以返回List<Integer>
,其返回类型为List
。这是因为List<Integer>
在运行时只是List
,因为类型擦除。
但是,如果你只是给出2 nd 方法实现,它将不满足第一种方法的契约。第一种方法说,它可以返回任何类型的List
,因为它在返回类型中使用了原始类型。因此,它可以返回List<Integer>
,List<Object>
,List<String>
,任何内容。但是2 nd 方法只能返回List<Integer>
。
编译器将在编译时执行此类型检查,它会向您发出警告,List
需要取消选中转换为List<Integer>
,因为由于类型擦除,转换无论如何都会在运行时成功。 / p>
这是类似的情况:
List<String> listString = new ArrayList<String>();
List rawList = new ArrayList();
listString = rawList; // Warning: Unchecked conversion
rawList = listString;
推荐阅读:
答案 1 :(得分:5)
答案在JLS 7 5.5.1. Reference Type Casting:
给定编译时引用类型S(源)和编译时 引用类型T(目标),如果存在从S到T的转换转换 由于以下规则,不会发生编译时错误。
如果S是班级类型:
If T is a class type, then either |S| <: |T|, or |T| <: |S|. Otherwise, a compile-time error occurs. Furthermore, if there exists a supertype X of T, and a supertype Y of S, such that both X and Y are provably distinct parameterized types
(§4.5),并且X和Y的擦除是相同的,编译时 发生错误。
在您的情况下,List<Integer>
和List<String>
是可证明不同的参数化类型,并且它们都具有相同的删除:List
。
答案 2 :(得分:4)
public List<Integer> getList()
的签名与类型擦除后public List getList()
的签名相同。对于重写方法,您需要覆盖方法作为重写方法的子签名。如果类型擦除后它们是相同的,那么这里的编译器将决定子类方法是否覆盖接口,并且我们永远不会发生冲突。
答案 3 :(得分:4)
这实际上是因为类型擦除而没有效果:
public interface Int1 {
public List getList();
}
public interface Int2 extends Int1 {
@Override
public List<Integer> getList();
}
在运行时,任何List<X>
都会变为List
。因此,你不要在这里@Override
做任何事情。 .getList()
的运行时原型是List getList()
。您在List
中参数化Int2
这一事实完全被忽略了。
编译器警告你:
java: Note: Main.java uses unchecked or unsafe operations.
java: Note: Recompile with -Xlint:unchecked for details.
答案 4 :(得分:2)
Int2扩展Int1然后List就可以了,
List<String>
无法编译,因为它未在Int1或Int2中定义。
List<Integer>
in
Int2
根据Java规范http://docs.oracle.com/javase/tutorial/java/generics/erasure.html
进行类型擦除答案 5 :(得分:0)
所以看起来它与类型擦除有关,它产生警告而不是编译器错误。我想这就像我之前见过的其他未经检查的转换场景。感谢人们研究得很好的答案。