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

Java 为什么这个类即使没有正确实现其接口也要编译?,java,generics,Java,Generics,那么下面的代码为什么要编译呢?当然MyClass2应该返回一个列表 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
应该返回一个
列表

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");
    }
  }
}
公共类主{
公共静态void main(最终字符串[]args){
MyClass MyClass=新的MyClass();
List=myClass.getList();
系统输出打印项次(列表);
System.out.println(list.get(0.getClass());
MyClass2 MyClass2=新的MyClass2();
List list2=myClass2.getList();
System.out.println(列表2);
System.out.println(list2.get(0.getClass());
}
公共接口Int1{
公共列表getList();
}
公共接口Int2扩展了Int1{
@凌驾
公共列表getList();
}
公共静态类MyClass实现Int2{
@凌驾
公共列表getList(){
返回数组.asList(1,2,3);
}
}
公共静态类MyClass2实现Int2{
@凌驾
公共列表getList(){
返回数组。asList(“一”、“二”、“三”);
}
}
}
我注意到,如果您试图将其设置为
列表
,则会出现一个错误“java:Main.MyClass2不是抽象的,并且不会覆盖Main.Int2中的抽象方法getList()。我不太明白为什么在上面的例子中你没有得到这个


注意:在我的项目中,问题的解决方案是使接口本身具有通用性,即
Int1
(当然,我使用了比这个更好的名称,这只是一个示例)

Int2扩展Int1,然后列表就可以了

List<String> 

根据Java规范,类型会被删除。我想,编译器应该警告您未经检查的转换,而不是将其标记为编译器错误。让我们了解为什么它会警告您:

为了满足所实现接口的契约,您需要实现以下两种方法:

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

建议阅读


类型擦除后,
公共列表getList()
的签名与
公共列表getList()
的签名相同。对于重写方法,您需要重写方法是被重写方法的子签名。这里的编译器将决定,如果在类型擦除之后它们是相同的,那么子类方法将重写接口方法,并且我们永远不会有冲突

答案是:

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

如果T是一个类类型,那么| S |这两种类型中的任何一种都不会因为类型擦除而产生影响:

public interface Int1 {
     public List getList();
}

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

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

您的编译器输出中通常应该有警告,因为Java泛型很糟糕。我不明白我们是如何“满足两个接口契约”的。一个接口覆盖另一个接口,所以肯定是
Int2
哪个
MyClass2
必须满足?对
列表的请求如何处理对
列表的请求?我知道在类型擦除之后它们是相同的,但在编译之前它们是不同的,因为在后者中,它会检查您添加到其中/从中删除的内容的类型,等等。@PaulRichards。是的,实际上,从技术上讲,您只实现了一个接口。它扩展了第一个接口。所以,你可以说你只需要满足单一接口的契约。正如我所说,返回类型为
List
的方法可以返回任何列表类型。因此,它还可以返回
列表
。这就是另一种方法的意义所在。它应该返回
List
,这由
List
负责。是的,它们在编译时是不同的。这就是为什么编译器会给您未检查的转换警告。因为它不确定两个列表是否相同。
List<String> listString = new ArrayList<String>();
List rawList = new ArrayList();

listString = rawList;  // Warning: Unchecked conversion
rawList = listString;
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
public interface Int1 {
     public List getList();
}

public interface Int2 extends Int1 {
     @Override
     public List<Integer> getList();
}
java: Note: Main.java uses unchecked or unsafe operations.
java: Note: Recompile with -Xlint:unchecked for details.