2017-03-05 52 views
4

這裏壓倒一切的抽象枚舉法是編譯正確編譯錯誤,而與一般的返回類型

public enum SupportedConversions { 
    INTEGER { 
     @Override 
     public Integer convert(String ion, Object[] aa) { 
      return null; 
     } 
    }, 
    LONG { 
     @Override 
     public Long convert(final String ion, Object[] aa) { 
      return null; 
     } 
    }; 

    public abstract <T> T convert(String val, Object[] aa); 
} 

但是,當我的抽象函數參數更改爲對象而不是數組的名單,我得到的編譯錯誤說一碼「方法不會從超類中覆蓋」。而這種情況只有在返回類型爲通用

例如糟糕的代碼

public enum SupportedConversions { 
    INTEGER { 
     @Override 
     public Integer convert(String ion, List<Object> aa) { 
      return null; 
     } 
    }, 
    LONG { 
     @Override 
     public Long convert(final String ion, List<Object> aa) { 
      return null; 
     } 
    }; 

    public abstract <T> T convert(String val, List<Object> aa); 
} 

是否有一個原因到不行。似乎更像是Java中的一個錯誤

+1

這很奇怪,雖然我確定有一個完全合乎邏輯的理由。如果你從'List'中移除類型參數,那麼編譯器錯誤就會消失。或者,您可以將類型參數設置爲「T」。數組的版本會發出如下警告:「未檢查重寫:返回類型需要未經檢查的轉換。找到'java.lang.Long',必需'T'」 – Jeremy

+0

是的編譯錯誤消失,如果我刪除顯式類型的列表壓倒一切的方法。 – Sparky

回答

1

問題應該是「爲什麼第一個可以編譯」而不是「爲什麼第二個失敗」。
兩者都壞了。

<T> T convert(String val, Object[] aa) 

方法簽名說:「不管呼叫者替代T,此方法將返回一個兼容的結果」。這不是很有用,因爲唯一有效的返回值是null,但至少,編譯器會告訴您,當您嘗試在以這種方式聲明的方法內返回不兼容的結果時。

但子類覆蓋此方法類似

Long convert(final String ion, Object[] aa) 
換句話說

,覆蓋,有望返回任何調用者與總是返回Long的方法希望的方法。這首先應該感覺不對......當您返回null時,結果仍然是兼容的,但當您返回非nullLong值時,編譯器甚至不會提醒您,因爲Long值與宣佈退貨類型爲Long

但是,編譯器應該已經發出了關於方法聲明本身的警告。爲了演示這個問題,你可以用這個聲明來編寫

String s = SupportedConversions.LONG.convert("bla", null); 

並且編譯器不會對象。如上所述,基本類型聲明<T> T convert(…)承諾返回任何呼叫者假設的T,這裏,T已被推斷爲String。這顯然會在運行時中斷,當實現返回一個Long實例。


爲什麼這個可以編譯的原因是與pre-Generics代碼的兼容性。目的是讓具有不同「生殖」狀態的圖書館互動。例如。您可以使用最近的jdk編譯Java 1.4應用程序代碼,即使某些類現在覆蓋 - 泛型方法。

因此,您的子類中不使用泛型的convert方法被允許覆蓋基類的convert方法。與此相反,像

Long convert(final String ion, List<Object> aa) 

方法聲明是使用泛型,因此,不允許繞過通用類型系統。如果您使用原始類型List,您又有一個非泛型聲明,可繞過泛型而不受影響。


如果您現在想說,假設這裏的泛型行爲是不合邏輯的,那麼您並不孤單。不僅因爲覆蓋方法與覆蓋的通用聲明位於相同的編譯單元(enum聲明)之內,兩者都在enum聲明內,這是一種在Java 5之前不存在的語法結構(引入泛型)。

此外,最重要的方法利用協變返回類型Long RESP。 Integer其中刪除方法聲明的返回類型爲Object,這也不能出現在Java 5之前的代碼中。

但是這些(仍然)是規則。您應該在這裏關注編譯器警告。如果您沒有收到警告(我知道Netbeans IDE的默認設置不嚴格),您應該嘗試啓用它們。


這段代碼沒有修正。 enum s不可能做什麼。您可以刪除類型參數T並讓基類型的方法聲明返回Object,但enum常量中的協變返回類型是不相關的,因爲它們不屬於public API的一部分。最好的選擇是:

public interface SupportedConversions<T> { 
    SupportedConversions<Integer> INTEGER = (String ion, Object[] aa) -> { 
     return null; 
    }; 
    SupportedConversions<Long> LONG = (String ion, Object[] aa) -> { 
     return null; 
    }; 
    public abstract T convert(String val, Object[] aa); 
} 

resp。

public interface SupportedConversions<T> { 
    SupportedConversions<Integer> INTEGER = (ion, aa) -> { 
     return null; 
    }; 
    SupportedConversions<Long> LONG = (ion, aa) -> { 
     return null; 
    }; 
    public abstract T convert(String val, List<Object> aa); 
    // we can support both variants 
    public default T convert(String val, Object[] aa) { 
     return convert(val, Arrays.asList(aa)); 
    } 
}