簡體   English   中英

Java - 從接口類型而不是類聲明

[英]Java - declaring from Interface type instead of Class

在我尋求正確掌握界面最佳實踐的過程中,我注意到以下聲明:

List<String> myList = new ArrayList<String>();

代替

ArrayList<String> myList = new ArrayList<String>();

-據我所知,原因是它允許靈活性,以防有一天您不想實現 ArrayList 而可能是另一種類型的列表。

用這個邏輯,我設置了一個例子:

public class InterfaceTest {

    public static void main(String[] args) {

        PetInterface p = new Cat();
        p.talk();

    }

}

interface PetInterface {                

    public void talk();

}

class Dog implements PetInterface {

    @Override
    public void talk() {
        System.out.println("Bark!");
    }

}

class Cat implements PetInterface {

    @Override
    public void talk() {
        System.out.println("Meow!");
    }

    public void batheSelf() {
        System.out.println("Cat bathing");
    }

}

我的問題是,我無法訪問 BatheSelf() 方法,因為它只存在於 Cat 中。 這讓我相信,如果我只打算使用在接口中聲明的方法(而不是來自子類的額外方法),我應該只從接口聲明,否則我應該直接從類聲明(在這種情況下為 Cat)。 我在這個假設中正確嗎?

當需要選擇通過interface引用對象還是通過class引用對象時,應首選前者,但前提是存在適當的類型

String implements CharSequence為例。 在所有情況下,您不應該盲目地使用CharSequence而不是String ,因為這會拒絕您進行簡單的操作,例如trim()toUpperCase()等。

然而,一個只需要一個String來關心它的char值序列的方法應該使用CharSequence代替,因為在這種情況下這是合適的類型。 實際上replace(CharSequence target, CharSequence replacement) String類中的replace(CharSequence target, CharSequence replacement)就是這種情況。

另一個例子是java.util.regex.Pattern及其Matcher matcher(CharSequence)方法。 這使得Matcher可以從Pattern創建,不僅用於String ,還用於所有其他CharSequence

在庫中應該使用interface但不幸沒有使用的一個很好的例子也可以在Matcher找到:它的appendReplacementappendTail方法只接受StringBuffer 自 1.5 以來,此類已在很大程度上被其更快的表親StringBuilder所取代。

StringBuilder不是StringBuffer ,因此我們不能將前者與Matcherappend…方法一起使用。 但是,它們都implements Appendable (也在 1.5 中引入)。 理想情況下Matcherappend…方法應該接受任何Appendable ,然后我們就可以使用StringBuilder以及所有其他可用的Appendable

因此,我們可以看到,當存在通過接口引用對象的適當類型時,這是一種強大的抽象,但前提是這些類型存在。 如果該類型不存在,那么您可以考慮定義自己的類型(如果有意義)。 例如,在這個Cat示例中,您可以定義interface SelfBathable 然后,而不是引用Cat ,您可以接受任何SelfBathable對象(例如Parakeet

如果創建一個新類型沒有意義,那么你可以通過它的class來引用它。

也可以看看

  • Effective Java 2nd Edition,Item 52:通過接口引用對象

    如果存在合適的接口類型,則參數、返回值和字段都應使用接口類型聲明。 如果你養成使用接口類型的習慣,你的程序就會靈活得多。 如果不存在適當的接口,則通過類引用對象是完全合適的。

相關鏈接

是的,你是對的。 您應該聲明為最通用的類​​型,提供您使用的方法。

這就是多態的概念。

您是對的,但如果需要,您可以從界面投射到所需的寵物。 例如:

PetInterface p = new Cat();
((Cat)p).batheSelf();

當然,如果您嘗試將您的寵物投給狗,則不能調用 BatheSelf() 方法。 它甚至不會編譯。 因此,為了避免出現問題,您可以使用這樣的方法:

public void bathe(PetInterface p){
    if (p instanceof Cat) {
        Cat c = (Cat) p;
        c.batheSelf();
    }
}

使用instanceof ,請確保不會在運行時嘗試讓狗自己洗澡。 這會引發錯誤。

是的,你是對的。 通過讓 Cat 實現“PetInterface”,您可以在上面的示例中使用它並輕松添加更多種類的寵物。 如果您確實需要特定於 Cat,則需要訪問 Cat 類。

您可以從 Cat 中的talk中調用方法batheSelf

通常,與具體類相比,您應該更喜歡接口。 沿着這些思路,如果您可以避免使用 new 運算符(它總是需要一個具體的類型,就像在您的新 ArrayList 示例中一樣),那就更好了。

這一切都與管理代碼中的依賴項有關。 最好只依賴高度抽象的事物(如接口),因為它們也往往非常穩定(請參閱http://objectmentor.com/resources/articles/stability.pdf )。 因為它們沒有代碼,所以只有在 API 更改時才必須更改它們……換句話說,當您希望該接口向世界呈現不同的行為時,即設計更改時。

另一方面,課程一直在變化。 依賴於類的代碼並不關心它如何做,只要 API 的輸入和輸出不改變,調用者就不應該關心。

您應該努力根據開閉原則(參見http://objectmentor.com/resources/articles/ocp.pdf )確定類的行為,這樣即使添加功能,現有接口也不需要更改,您可以指定一個新的子接口。

避免使用 new 運算符的舊方法是使用抽象工廠模式,但這有其自身的一系列問題。 更好的是使用像 Guice 這樣的工具進行依賴注入,並且更喜歡構造函數注入。 在開始使用依賴注入之前,請確保您了解依賴倒置原則(請參閱http://objectmentor.com/resources/articles/dip.pdf )。 我見過很多人注入了不適當的依賴項,然后抱怨這個工具對他們沒有幫助……它不會讓你成為一個偉大的程序員,你仍然必須適當地使用它。

示例:您正在編寫一個幫助學生學習物理的程序。 在這個程序中,學生可以將一個球放在各種物理場景中並觀察它的行為:從懸崖上的大炮中射出它,把它放在水下,在深空等等。 問題:你想包括一些關於球的重量的東西Ball API 中的球……您應該包含 getMass() 方法還是 getWeight() 方法?

重量取決於球碰巧所處的環境。調用者可以方便地調用一個方法並在任何地方獲取球的重量,但是如何編寫這個方法呢? 每個球實例必須不斷跟蹤它的位置以及當前的重力常數是多少。 所以你應該更喜歡 getMass(),因為質量是球的固有屬性,不依賴於它的環境。

等等,如果你只使用 getWeight(Environment) 呢? 這樣,球實例就可以從環境中取出當前的 g 並繼續……更好的是,您可以使用 Guice 在球的構造函數中注入環境! 這是我經常看到的濫用類型,人們最終指責 Guice 無法像他們希望的那樣無縫地處理依賴注入。

這里的問題不是 Guice,而是 Ball API 設計。 重量不是球的固有屬性,因此它不是應該可以從球獲得的屬性。 相反,Ball 應該使用 getMass() 方法實現 MassiveObject 接口,而 Environment 應該有一個名為 getWeightOf(MassiveObject) 的方法。 環境的內在是它自己的萬有引力常數,所以這要好得多。 而 Environment 現在只依賴於一個簡單的接口,MassiveObject……但它的工作是包含對象,所以這應該是。

為什么不簡單地這樣做!

Cat c = new Cat();
PetInterface p = (PetInterface)c;
p.talk();
c.batheSelf();

現在我們只有一個對象,可以使用 2 個引用來操作它。
引用 p 可用於調用接口中定義的函數,而 c 僅可用於調用類(或超類)中定義的函數。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM