2015-09-04 91 views
1

爲什麼我們需要在Java 8接口後衛方法,因爲我們已經有抽象classes.I發現的各種答案,在互聯網上,如:的Java 8極品

添加外部功能

但是抽象類是用於部分抽象的,因爲我們的接口實際上是一個純抽象類,所以爲什麼它們是接口中的默認方法?

+1

@Siguza這兩種是同義詞,而「默認」是官方的。 – dasblinkenlight

+0

通常的答案(這很容易找到)是他們需要將方法添加到像「Collection」這樣的接口;沒有默認的方法會破壞大量的第三方類,這些類會實現'collection'而不是新的方法。 – SJuan76

+0

@VGR不是一個騙局,但一個答案解釋得很好 – SJuan76

回答

2

通過將功能放置在抽象基類中來共享功能的問題是一個類可以從一個基類派生。當您想從多個基地繼承功能時,這是一個限制。

當您需要從已具有基類的類實現接口時,通過抽象基類共享功能也可能成爲問題。在這種情況下,你根本無法派生你的新課程,因爲你必須從兩個基地中選擇一個,當時你想要兩個基地。

默認方法用優雅來解決這個問題:將您的通用實現置於默認方法中可讓您無限制地共享代碼。

你能想到的默認方法之間的主要區別和繼承一個抽象類作爲跨實現相同的接口,或者垂直從同一個基類繼承的兒童的兄弟姐妹共享功能水平之間的差異。

這裏是一個考試:考慮一個接口,看起來像JDBC的ResultSet,它有兩種訪問同一列的方式 - 按名稱和索引。該接口可以被編碼成這樣:

interface ResultSet2 { 
    int findColumn(String columnLabel); 
    String getString(int index); 
    long getLong(int index); 
    default long getLong(String columnLabel) { 
     return getLong(findColumn(columnLabel)); 
    } 
    default String getString(String columnLabel) { 
     return getString(findColumn(columnLabel)); 
    } 
} 

任何實施ResultSet2必須實現三個方法,並獲得剩下的兩個是免費的。他們可以選擇提供替代實施方案,但這是可選的。

+0

謝謝你的回答,我認爲現在它清楚:) –

1

defender方法背後的主要原因是能夠使用新功能擴展長期存在的接口,而不會破壞現有的代碼。特別是在Java 8 lamba表達式中,他們在集合接口上引入了很多新方法,如Iterable.forEach。通過提供默認方法,實現Iterable接口的現有類不必更改爲在Java 8環境中使用。

+0

你錯了「沒有打破現有的代碼」。如果向接口添加一些已在某些實現類中定義的方法,並且這些方法與它們不兼容(例如它具有不同的結果類型),則會斷開現有代碼。你有沒有聽說過開放/封閉原則?當他們設計出如此厚的語言特性時,Brian Goetz和Oracle中的其他人肯定沒有聽說過它。 – mentallurg

1

最初的意圖是與C#的擴展方法競爭。給定接口的核心方法,例如get(), set() in List,擴展方法(例如sort())可以被定義和實現。

Java人認爲在接口本身而不是外部聲明這樣的方法會更好,以便這些方法可以被子類型覆蓋,爲每個子類型提供最佳的實現。 (他們也認爲這樣的方法應該由界面作者控制;這是一個軟點)

雖然可以將默認方法添加到現有接口,但是破壞現有第三方子類型非常危險,特別是對於非常像List這樣的舊類型在野外有許多亞型。因此很少有默認方法被添加到現有的核心Java API中。請參閱this question

對於新界面,默認方法對API設計人員來說是非常有價值的工具。您可以爲接口添加許多便利方法,例如,Function.compose()。子類只需要實現抽象/核心方法,而不是默認方法(但如果他們想要的話,它們可以)。

我不同意默認方法可以「演化」接口的想法。它們不會更改接口的核心語義,它們只是便捷方法(以實例方法的形式)。

默認方法應該在設計接口時預先仔細設計;如上所述,之後添加默認方法是非常危險的。


C#的擴展方法允許第三方添加便捷方法;這是非常好的,沒有理由說Java未來不會推出類似的東西。