簡體   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