繁体   English   中英

为什么这个类编译即使它没有正确实现其接口?

[英]Why does this class compile even though it does not properly implement its interface?

一个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> (当然我使用比这更好的名称,它只是一个例子)。

我怀疑,编译器应该为您提供Unchecked转换的警告,而不是将其标记为编译器错误。 让我们理解为什么它会警告你:

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

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

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

但是,如果你只是给第二个方法实现,它将不满足第一个方法的合同。 第一种方法说,它可以返回任何类型的List ,因为它在返回类型中使用了raw类型。 因此,它可以返回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;

推荐阅读

答案在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

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

从字面上看,由于类型擦除,这没有任何影响:

public interface Int1 {
     public List getList();
}

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

在运行时,任何List<X>将成为List 因此,你不要在这里@Override任何东西。 .getList()的运行时原型是List getList() 您在Int2参数化List的事实将被完全忽略。

编译器警告你:

java: Note: Main.java uses unchecked or unsafe operations.
java: Note: Recompile with -Xlint:unchecked for details.

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

List<String> 

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

List<Integer> 

Int2 

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

所以看起来它与类型擦除有关,它产生警告而不是编译器错误。 我想这就像我之前见过的其他未经检查的转换场景。 感谢人们研究得很好的答案。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM