CodeGym /Java Blog /Toto sisi /為什麼我們需要 Java 中的接口
John Squirrels
等級 41
San Francisco

為什麼我們需要 Java 中的接口

在 Toto sisi 群組發布
你好!今天我們要講的是Java中一個重要的概念:接口。這個詞你可能很熟悉。例如,大多數計算機程序和遊戲都有界面。從廣義上講,接口是一種連接兩個交互方的“遙控器”。日常生活中界面的一個簡單示例是電視遙控器。它連接兩個對象——一個人和一台電視——並執行不同的任務:調高或調低音量、切換頻道以及打開或關閉電視。一方(人)需要訪問界面(按遙控器上的按鈕)才能使第二方執行操作。例如,讓電視切換到下一個頻道。更重要的是,用戶不 不需要知道電視是如何組織的或者頻道切換過程是如何在內部實現的。用戶唯一可以訪問的是界面。主要目標是獲得所需的結果。這與編程和 Java 有什麼關係?一切 :) 創建接口與創建常規類非常相似,但使用的是單詞,我們指示單詞interface。讓我們看看最簡單的 Java 接口,看看它是如何工作的,以及我們為什麼需要它:

public interface CanSwim {

     public void swim();
}
我們已經創建了一個CanSwim界面。它有點像我們的遙控器,但有一個“按鈕”:swim()方法。但是我們如何使用這個遙控器呢?為此,我們需要實現一個方法,即我們的遙控器按鈕。要使用接口,我們程序中的一些類必須實現它的方法。讓我們發明一個對象“會游泳”的類。例如,一個Duck類適合:

public class Duck implements CanSwim {

    public void swim() {
        System.out.println("Duck, swim!");
    }

    public static void main(String[] args) {

        Duck duck = new Duck();
        duck.swim();
    }
}
“我們在這裡看到了什麼?Duck類通過implements關鍵字與CanSwim接口‘關聯’ 。您可能還記得我們使用類似的機制通過繼承關聯兩個類,但在那種情況下我們使用了擴展這個詞。對於完全清楚,我們可以將“ public class Duck implements CanSwim ”直譯為:“public Duck class implements CanSwim interface”。這意味著與接口關聯的類必須實現其所有方法。注意:我們的類,就像接口,有一個方法,它包含一些邏輯。這是強制要求。如果我們只寫DuckCanSwimswim()public class Duck implements CanSwim沒有swim()Duck類中創建方法,編譯器會給我們一個錯誤: Duck is not abstract and does not override abstract method swim() in CanSwim 為什麼?為什麼會這樣?如果我們使用電視示例來解釋該錯誤,這就像遞給某人一個帶有“更改頻道”按鈕但無法更改頻道的電視遙控器。您可以隨心所欲地按下按鈕,但它不起作用。遙控器本身不換台:它只是向電視發送一個信號,電視實現複雜的換台過程。我們的鴨子也是如此:它必須知道如何游泳才能使用CanSwim界面調用它。如果它不知道如何,CanSwim接口不連接兩方——人和程序。此人將無法使用該方法在程序內游泳swim()Duck現在你更清楚接口是做什麼用的了。接口描述了實現該接口的類必須具有的行為。“行為”是方法的集合。如果我們想創建多個信使,最簡單的方法就是創建一個Messenger界面。每個使者需要什麼?在基本層面上,他們必須能夠接收和發送消息。

public interface Messenger{

     public void sendMessage();

     public void getMessage();
}
現在我們可以簡單地創建實現相應接口的信使類。編譯器本身將“強制”我們在我們的類中實現它們。電報:

public class Telegram implements Messenger {

    public void sendMessage() {

        System.out.println("Sending a Telegram message!");
    }

     public void getMessage() {
         System.out.println("Receiving a Telegram message!");
     }
}
微信:

public class WhatsApp implements Messenger {

    public void sendMessage() {

        System.out.println("Sending a WhatsApp message!");
    }

     public void getMessage() {
         System.out.println("Reading a WhatsApp message!");
     }
}
微博:

public class Viber implements Messenger {

    public void sendMessage() {

        System.out.println("Sending a Viber message!");
    }

     public void getMessage() {
         System.out.println("Receiving a Viber message!");
     }
}
這提供了哪些優勢?其中最重要的是松耦合。想像一下,我們正在設計一個將收集客戶數據的程序。該類Client肯定需要一個字段來指示客戶端正在使用的特定信使。如果沒有接口,這看起來很奇怪:

public class Client {

    private WhatsApp whatsApp;
    private Telegram telegram;
    private Viber viber;
}
我們創建了三個字段,但一個客戶端只能有一個信使。我們只是不知道是哪一個。所以我們必須在類中添加每一種可能性,以便能夠與客戶端進行通信。事實證明,其中一兩個將永遠是null程序完全不需要的。最好改用我們的界面:

public class Client {

    private Messenger messenger;
}
這是一個鬆散耦合的例子!我們沒有在類中指定特定的信使類Client,而只是表明客戶端有一個信使。究竟是哪一個將在程序運行時確定。但是為什麼我們需要接口呢?為什麼他們甚至被添加到語言中?這是個好問題——也是正確的問題!我們不能使用普通的繼承來達到相同的結果嗎?該類Messenger作為父級,並且ViberTelegramWhatsApp作為子級。的確,這是可能的。但是有一個障礙。如您所知,Java 沒有多重繼承。但是有對多個接口的支持。一個類可以實現任意多個接口。想像一下,我們有一個Smartphone類有一個App字段,表示安裝在智能手機上的應用程序。

public class Smartphone {

    private App app;
}
當然,應用程序和 Messenger 很相似,但它們仍然是不同的東西。Messenger 可以有移動版和桌面版,但 App 專門代表移動應用程序。事情是這樣的——如果我們使用繼承,我們將無法TelegramSmartphone類中添加對象。畢竟,該類Telegram不能同時繼承AppMessenger!我們已經讓它繼承Messenger並添加到Client類中。但是Telegram類可以輕鬆實現這兩個接口!因此,我們可以給Client類一個Telegram對像作為 a Messenger,我們可以把它Smartphone作為 an 給類App。這是你如何做到的:

public class Telegram implements Application, Messenger {

    // ...methods
}

public class Client {

    private Messenger messenger;

    public Client() {
        this.messenger = new Telegram();
    }
}


public class Smartphone {

    private Application application;

    public Smartphone() {
        this.application = new Telegram();
    }
}
現在我們正在使用Telegram我們想要的類。在某些地方,它充當App. 在其他地方,它充當Messenger. 您肯定已經註意到接口方法總是“空的”,即它們沒有實現。原因很簡單:接口描述了行為,但沒有實現它。“所有實現CanSwim接口的對像都必須能夠游泳”:這就是接口告訴我們的全部內容。魚、鴨子和馬游泳的具體方式是FishDuckHorse類,而不是接口。就像換頻道是電視的任務一樣。遙控器只是為您提供一個按鈕。然而,Java 8 中出現了一個有趣的新增功能——默認方法。例如,您的接口有 10 個方法。其中 9 個在不同的類中有不同的實現,但一個對所有類的實現都是相同的。以前,在 Java 8 之前,接口方法沒有任何實現:編譯器立即給出錯誤。現在你可以這樣做:

public interface CanSwim {

   public default void swim() {
       System.out.println("Swim!");
   }

   public void eat();

   public void run();
}
使用default關鍵字,我們創建了一個具有默認實現的接口方法。我們需要在eat()所有run()實現CanSwim. 我們不需要用swim()方法來做這個:每個類的實現都是一樣的。順便說一下,您已經在過去的任務中遇到過接口,即使您沒有註意到 :) 這是一個生動的示例: 為什麼接口在 Java 中是必需的 - 2您已經使用了ListSet接口!更準確地說,您已經使用了它們的實現 — ArrayListLinkedListHashSet等。同一張圖清楚地給出了一個示例,其中一個類同時實現了多個接口。例如,LinkedList實現ListDeque(雙端隊列)接口。您熟悉界面Map,或者更確切地說,熟悉它的HashMap實現。順便說一句,這張圖說明了一個特點:接口可以繼承其他接口。接口SortedMap繼承Map,同時Deque繼承Queue。如果你想顯示接口之間的關係,這是必要的,其中一個接口是另一個接口的擴展版本。 讓我們考慮一個帶有Queue接口的例子。我們還沒有審核Queues,但它相當簡單,就像商店裡的普通隊列或線路一樣工作。您只能將項目添加到隊列的末尾,並且只能從頭開始取。在某些時候,開發人員需要隊列的增強版本,以便在兩端添加和獲取項目。所以他們創建了一個Deque接口,它是一個雙端隊列。它具有普通隊列的所有方法。畢竟是雙端隊列的父類,不過也增加了新的方法。
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION