13

一个列表然后为什么代码(下面)编译?当然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");
    }
  }
}

我注意到,如果您尝试将其设为 a List<String>,则会收到错误消息“java:Main.MyClass2 不是抽象的,并且不会覆盖 Main.Int2 中的抽象方法 getList()”。我不太明白为什么你在上面的例子中没有得到这个。

注意:我项目中问题的解决方案是让接口本身通用,即Int1<X>(当然我用的名字比这个更好,只是一个例子)。

4

6 回答 6

7

我怀疑,编译器应该给你Unchecked conversion的警告,而不是把它标记为编译器错误。让我们了解它为什么会警告您:

您有以下两种方法来实现以满足您实现的接口的合同:

public List getList();
public List<Integer> getList();

现在,如果在您的类中,您只提供第一种方法的实现,它可以处理对第二种方法的请求。您可以返回 a List<Integer>,返回类型为List。那是因为 a在运行时List<Integer>只是 a ,因为类型擦除List

但是,如果你只给出第二种方法的实现,它不会满足第一种方法的约定。第一种方法说,它可以返回任何类型的List,因为它在返回类型中使用了原始类型。所以,它可以返回List<Integer>, List<Object>, List<String>, 任何东西。但是第二种方法只能返回List<Integer>

编译器将在编译时进行这种类型检查,它会给你警告,List需要未经检查的转换为List<Integer>,因为由于类型擦除,转换无论如何都会在运行时成功。


这是类似的情况,如下所示:

List<String> listString = new ArrayList<String>();
List rawList = new ArrayList();

listString = rawList;  // Warning: Unchecked conversion
rawList = listString;

推荐阅读

于 2013-07-18T08:22:15.573 回答
5

答案在JLS 7 5.5.1 中。参考类型转换

给定一个编译时引用类型 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.

于 2013-07-18T08:23:48.240 回答
4

的签名与类型擦除后public List<Integer> getList()的签名相同。public List getList()对于覆盖方法,您需要覆盖方法是被覆盖方法的子签名。如果在类型擦除后它们相同并且我们永远不会发生冲突,那么这里的编译器将决定子类方法确实覆盖接口一。

于 2013-07-18T08:22:22.063 回答
4

从字面上看,由于类型擦除,这没有效果:

public interface Int1 {
     public List getList();
}

public interface Int2 extends Int1 {
     @Override
     public List<Integer> getList();
}

在运行时,anyList<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.
于 2013-07-18T08:23:49.400 回答
2

Int2 扩展 Int1 然后 List 就可以了,

List<String> 

无法编译,因为它没有在 Int1 或 Int2 中定义。

List<Integer> 

Int2 

根据 Java 规范http://docs.oracle.com/javase/tutorial/java/generics/erasure.html需要进行类型擦除

于 2013-07-18T08:21:46.483 回答
0

因此,它看起来与类型擦除有关,并且它会产生警告而不是编译器错误。我想这就像我以前见过的其他未经检查的转换场景一样。感谢人们精心研究的答案。

于 2013-07-19T09:58:06.060 回答