CodeGym /Java blogg /Slumpmässig /Varför behöver vi gränssnitt i Java
John Squirrels
Nivå
San Francisco

Varför behöver vi gränssnitt i Java

Publicerad i gruppen
Hej! Idag ska vi prata om ett viktigt koncept i Java: gränssnitt. Ordet är förmodligen bekant för dig. Till exempel har de flesta datorprogram och spel gränssnitt. I vid mening är ett gränssnitt ett slags "fjärrkontroll" som kopplar samman två interagerande parter. Ett enkelt exempel på ett gränssnitt i vardagen är en TV-fjärrkontroll. Den kopplar samman två objekt — en person och en TV — och utför olika uppgifter: höja eller sänka volymen, byta kanal och slå på eller stänga av TV:n. En part (personen) behöver komma åt gränssnittet (tryck på en knapp på fjärrkontrollen) för att få den andra parten att utföra åtgärden. Till exempel för att få TV:n att byta till nästa kanal. Dessutom gör inte användaren t behöver veta hur TV:n är organiserad eller hur kanalbytesprocessen implementeras internt. Det enda användaren har tillgång till är gränssnittet. Huvudsyftet är att få det önskade resultatet. Vad har detta med programmering och Java att göra? Allt :) Att skapa ett gränssnitt är väldigt likt att skapa en vanlig klass, men istället använda ordetklass anger vi ordet gränssnitt . Låt oss titta på det enklaste Java-gränssnittet, se hur det fungerar och varför vi skulle behöva det:

public interface CanSwim {

     public void swim();
}
Vi har skapat ett CanSwim- gränssnitt. Det är lite som vår fjärrkontroll, men med en "knapp": metoden swim() . Men hur använder vi den här fjärrkontrollen? För att göra detta behöver vi implementera en metod, det vill säga vår fjärrkontrollknapp. För att använda ett gränssnitt måste vissa klasser i vårt program implementera dess metoder. Låt oss uppfinna en klass vars föremål "kan simma". Till exempel passar en Duck -klass:

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();
    }
}
"Vad ser vi här? Duck -klassen är 'associerad' med CanSwim -gränssnittet av nyckelordet implements . Du kanske minns att vi använde en liknande mekanism för att associera två klasser genom arv, men i så fall använde vi ordet extends. För fullständig tydlighet kan vi översätta ' public class Duck implements CanSwim ' bokstavligen som: 'The public Duck -klassen implementerar CanSwim -gränssnittet'. Detta betyder att en klass som är associerad med ett gränssnitt måste implementera alla dess metoder. Notera: vår Duckklass, precis som gränssnittet CanSwim, har en swim()metod, och det innehåller viss logik. Detta är ett obligatoriskt krav. Om vi ​​bara skriverpublic class Duck implements CanSwimutan att skapa en swim()metod i Duckklassen kommer kompilatorn ge oss ett fel: Duck är inte abstrakt och åsidosätter inte abstrakt metod swim() i CanSwim Varför? Varför händer detta? Om vi ​​förklarar felet med hjälp av TV-exemplet skulle det vara som att ge någon en TV-fjärrkontroll med en "byt kanal"-knapp som inte kan byta kanal. Du kan trycka på knappen hur mycket du vill, men det fungerar inte. Fjärrkontrollen byter inte kanal av sig själv: den skickar bara en signal till TV:n, vilket implementerar den komplexa processen med kanalbyte. Och så är det med vår anka: den måste kunna simma så att den kan kallas med hjälp av gränssnittet CanSwim. Om den inte vet hurCanSwimgränssnittet kopplar inte samman de två parterna – personen och programmet. Personen kommer inte att kunna använda swim()metoden för att simma Ducki programmet. Nu förstår du tydligare vad gränssnitt är till för. Ett gränssnitt beskriver beteendet som klasser som implementerar gränssnittet måste ha. 'Beteende' är en samling metoder. Om vi ​​vill skapa flera budbärare är det enklaste att skapa ett Messengergränssnitt. Vad behöver varje budbärare? På en grundläggande nivå ska de kunna ta emot och skicka meddelanden.

public interface Messenger{

     public void sendMessage();

     public void getMessage();
}
Nu kan vi helt enkelt skapa våra messenger-klasser som implementerar motsvarande gränssnitt. Kompilatorn i sig kommer att "tvinga" oss att implementera dem i våra klasser. Telegram:

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!");
     }
}
WhatsApp:

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!");
     }
}
Viber:

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!");
     }
}
Vilka fördelar ger detta? Den viktigaste av dem är lös koppling. Föreställ dig att vi designar ett program som samlar in kunddata. Klassen Clientbehöver definitivt ett fält för att indikera vilken specifik budbärare klienten använder. Utan gränssnitt skulle det här se konstigt ut:

public class Client {

    private WhatsApp whatsApp;
    private Telegram telegram;
    private Viber viber;
}
Vi skapade tre fält, men en klient kan bara ha en budbärare. Vi vet bara inte vilken. Så vi måste lägga till alla möjligheter till klassen för att kunna kommunicera med kunden. Det visar sig att en eller två av dem alltid kommer att vara , nullhelt onödiga av programmet. Det är bättre att använda vårt gränssnitt istället:

public class Client {

    private Messenger messenger;
}
Detta är ett exempel på lös koppling! Istället för att ange en specifik budbärarklass i Clientklassen, anger vi bara att klienten har en budbärare. Vilken exakt kommer att avgöras medan programmet körs. Men varför behöver vi gränssnitt för detta? Varför lades de ens till språket? Det är en bra fråga - och den rätta frågan! Kan vi inte uppnå samma resultat med vanligt arv? Klassen Messengersom förälder, och Viber, Telegram, och WhatsAppsom barn. Det är faktiskt möjligt. Men det finns en hake. Som du redan vet har Java inget multipelt arv. Men det finns stöd för flera gränssnitt. En klass kan implementera så många gränssnitt du vill. Föreställ dig att vi har en Smartphoneklass som har enAppfält, som representerar en app installerad på smarttelefonen.

public class Smartphone {

    private App app;
}
Naturligtvis är en app och en budbärare lika, men de är fortfarande olika saker. Det kan finnas mobil- och stationära versioner av en messenger, men App representerar specifikt en mobilapp. Här är affären – om vi använde arv skulle vi inte kunna lägga till ett Telegramobjekt i Smartphoneklassen. Klassen kan ju Telegraminte samtidigt ärva Appoch Messenger! Och vi har redan gjort det i arv Messengeroch lagt till det i Clientklassen. Men Telegramklassen kan enkelt implementera båda gränssnitten! Följaktligen kan vi ge Clientklassen ett Telegramobjekt som ett Messenger, och vi kan ge det till Smartphoneklassen som ett App. Så här gör du det:

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();
    }
}
Nu använder vi Telegramklassen som vi vill. På vissa ställen fungerar den som en App. På andra ställen fungerar den som en Messenger. Du har säkert redan märkt att gränssnittsmetoder alltid är "tomma", dvs de har ingen implementering. Anledningen till detta är enkel: gränssnittet beskriver beteende, men det implementerar det inte. "Alla objekt som implementerar CanSwimgränssnittet måste kunna simma": det är allt som gränssnittet säger oss. Det specifika sättet som fiskar, ankor och hästar simmar är en fråga för , Fish, DuckochHorseklasser, inte gränssnittet. Precis som att byta kanal är en uppgift för TV:n. Fjärrkontrollen ger dig helt enkelt en knapp för detta. Men ett intressant tillägg dök upp i Java 8 — standardmetoder. Ditt gränssnitt har till exempel 10 metoder. 9 av dem har olika implementeringar i olika klasser, men en är implementerad likadan för alla. Tidigare, före Java 8, hade gränssnittsmetoder ingen som helst implementering: kompilatorn gav omedelbart ett fel. Nu kan du göra något så här:

public interface CanSwim {

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

   public void eat();

   public void run();
}
Med hjälp av defaultnyckelordet har vi skapat en gränssnittsmetod med en standardimplementering. Vi måste tillhandahålla vår egen implementering för två andra metoder – eat()och run()– i alla klasser som implementerar CanSwim. Vi behöver inte göra detta med metoden swim(): implementeringen kommer att vara densamma i varje klass. Förresten, du har redan stött på gränssnitt i tidigare uppgifter, även om du inte märkt det :) Här är ett levande exempel: Varför gränssnitt är nödvändiga i Java - 2Du har arbetat med gränssnitten Listoch ! SetMer exakt, du har arbetat med deras implementeringar — ArrayList, LinkedList, HashSet, etc. Samma diagram ger tydligt ett exempel där en klass implementerar flera gränssnitt samtidigt. Till exempel LinkedListimplementerar ListochDeque(dubbla köer) gränssnitt. Du är bekant med gränssnittet, Mapeller snarare, med dess HashMapimplementering. Förresten, detta diagram illustrerar en funktion: gränssnitt kan ärva andra gränssnitt. Gränssnittet SortedMapärver Mapmedan Dequedet ärver Queue. Detta är nödvändigt om du vill visa förhållandet mellan gränssnitt, där ett gränssnitt är en utökad version av ett annat. Låt oss överväga ett exempel med Queuegränssnittet. Vi har inte granskat ännuQueues, men det är ganska enkelt och fungerar som en vanlig kö, eller kö, i en butik. Du kan bara lägga till objekt i slutet av kön och kan bara ta dem från början. Vid något tillfälle behövde utvecklare en förbättrad version av kön för att lägga till och ta objekt i båda ändar. Så de skapade ett Dequegränssnitt, som är en dubbeländad kö. Den har alla metoder för en vanlig kö. Det är trots allt föräldern till dubbelkön, men den lägger också till nya metoder.
Kommentarer
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION