CodeGym /Blog Java /Aleatoriu /De ce avem nevoie de interfețe în Java
John Squirrels
Nivel
San Francisco

De ce avem nevoie de interfețe în Java

Publicat în grup
Bună! Astăzi vom vorbi despre un concept important în Java: interfețele. Cuvântul vă este, probabil, familiar. De exemplu, majoritatea programelor de calculator și a jocurilor au interfețe. Într-un sens larg, o interfață este un fel de „control de la distanță” care conectează două părți care interacționează. Un exemplu simplu de interfață în viața de zi cu zi este telecomanda televizorului. Conectează două obiecte — o persoană și un televizor — și îndeplinește diferite sarcini: crește sau reduce volumul, schimbă canalele și pornește sau opri televizorul. O parte (persoana) trebuie să acceseze interfața (apăsați un buton de pe telecomandă) pentru a determina a doua parte să efectueze acțiunea. De exemplu, pentru a face ca televizorul să treacă la următorul canal. În plus, utilizatorul nu Nu trebuie să știu cum este organizat televizorul sau cum este implementat intern procesul de schimbare a canalului. Singurul lucru la care utilizatorul are acces este interfața. Obiectivul principal este obținerea rezultatului dorit. Ce legătură are asta cu programarea și Java? Totul :) Crearea unei interfețe este foarte asemănătoare cu crearea unei clase obișnuite, dar în schimb folosirea cuvântuluiclasa , indicăm cuvântul interfață . Să ne uităm la cea mai simplă interfață Java, să vedem cum funcționează și de ce am avea nevoie de ea:

public interface CanSwim {

     public void swim();
}
Am creat o interfață CanSwim . Este un pic ca telecomanda noastră, dar cu un singur „buton”: metoda swim() . Dar cum folosim această telecomandă? Pentru a face acest lucru, trebuie să implementăm o metodă, adică butonul nostru de telecomandă. Pentru a utiliza o interfață, unele clase din programul nostru trebuie să implementeze metodele acesteia. Să inventăm o clasă ale cărei obiecte „pot înota”. De exemplu, o clasă Duck se potrivește:

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();
    }
}
„Ce vedem aici? Clasa Duck este „asociată” cu interfața CanSwim prin cuvântul cheie implements . Poate vă amintiți că am folosit un mecanism similar pentru a asocia două clase prin moștenire, dar în acest caz am folosit cuvântul extins. Pentru claritate completă, putem traduce „ clasa publică Duck implementează CanSwim ” literal ca: „Clasa publică Duck implementează interfața CanSwim ”. Aceasta înseamnă că o clasă asociată cu o interfață trebuie să implementeze toate metodele acesteia. Notă: Duckclasa noastră, la fel ca interfața CanSwim, are o swim()metodă și conține ceva logică.Aceasta este o cerință obligatorie.Dacă scriem doarpublic class Duck implements CanSwimfără a crea o swim()metodă în Duckclasă, compilatorul ne va da o eroare: Duck nu este abstract și nu suprascrie metoda abstractă swim() în CanSwim De ce? De ce se întâmplă asta? Dacă explicăm eroarea folosind exemplul TV, ar fi ca și cum ai înmâna cuiva o telecomandă a televizorului cu un buton „schimba canalul” care nu poate schimba canalele. Ai putea apăsa butonul cât de mult vrei, dar nu va funcționa. Telecomanda nu schimbă canalele de la sine: trimite doar un semnal către televizor, care implementează procesul complex de schimbare a canalului. Și așa este și cu rata noastră: trebuie să știe să înoate pentru a putea fi numită folosind interfața CanSwim. Dacă nu știe cum,CanSwiminterfața nu conectează cele două părți - persoana și programul. Persoana nu va putea folosi swim()metoda pentru a face o Duckbaie în cadrul programului. Acum înțelegeți mai clar pentru ce sunt interfețele. O interfață descrie comportamentul pe care trebuie să îl aibă clasele care implementează interfața. „Comportamentul” este o colecție de metode. Dacă vrem să creăm mai mulți mesageri, cel mai ușor lucru de făcut este să creăm o Messengerinterfață. De ce are nevoie fiecare mesager? La un nivel de bază, trebuie să poată primi și trimite mesaje.

public interface Messenger{

     public void sendMessage();

     public void getMessage();
}
Acum putem crea pur și simplu clasele noastre de mesagerie care implementează interfața corespunzătoare. Compilatorul însuși ne va „forța” să le implementăm în clasele noastre. 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!");
     }
}
Ce avantaje oferă acest lucru? Cel mai important dintre ele este cuplajul liber. Imaginați-vă că proiectăm un program care va colecta date despre clienți. Clasa Clientare nevoie cu siguranță de un câmp pentru a indica ce mesager specific folosește clientul. Fără interfețe, asta ar părea ciudat:

public class Client {

    private WhatsApp whatsApp;
    private Telegram telegram;
    private Viber viber;
}
Am creat trei câmpuri, dar un client poate avea un singur messenger. Doar că nu știm care dintre ele. Așa că trebuie să adăugăm toate posibilitățile clasei pentru a putea comunica cu clientul. Se dovedește că unul sau două dintre ele vor fi întotdeauna null, complet inutile de program. Este mai bine să folosiți interfața noastră:

public class Client {

    private Messenger messenger;
}
Acesta este un exemplu de cuplare liberă! În loc să specificăm o anumită clasă de mesagerie în Clientclasă, indicăm doar că clientul are un mesager. Care dintre ele va fi determinată în timp ce programul rulează. Dar de ce avem nevoie de interfețe pentru asta? De ce au fost adăugate la limbă? Aceasta este o întrebare bună - și întrebarea corectă! Nu putem obține același rezultat folosind moștenirea obișnuită? Clasa Messengerca părinte și Viber, Telegramși WhatsAppca copii. Într-adevăr, este posibil. Dar există o problemă. După cum știți deja, Java nu are moștenire multiplă. Dar există suport pentru mai multe interfețe. O clasă poate implementa câte interfețe doriți. Imaginează-ți că avem o Smartphoneclasă care are unaAppcâmp, care reprezintă o aplicație instalată pe smartphone.

public class Smartphone {

    private App app;
}
Desigur, o aplicație și un messenger sunt similare, dar sunt încă lucruri diferite. Pot exista versiuni mobile și desktop ale unui messenger, dar App reprezintă în mod specific o aplicație mobilă. Iată afacerea - dacă am folosi moștenirea, nu am putea adăuga un Telegramobiect la Smartphoneclasă. La urma urmei, Telegramclasa nu poate moșteni simultan Appși Messenger! Și deja l-am făcut să moștenească Messengerși l-am adăugat la Clientclasă. Dar Telegramclasa poate implementa cu ușurință ambele interfețe! În consecință, putem da Clientclasei un Telegramobiect ca Messenger, și îl putem da clasei Smartphoneca App. Iată cum faci asta:

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();
    }
}
Acum folosim Telegramclasa cum vrem. În unele locuri, acționează ca un App. În alte locuri, acționează ca un Messenger. Cu siguranță ați observat deja că metodele de interfață sunt întotdeauna „vide”, adică nu au nicio implementare. Motivul este simplu: interfața descrie comportamentul, dar nu îl implementează. „Toate obiectele care implementează interfața CanSwimtrebuie să poată înota”: atât ne spune interfața. Modul specific în care peștii, rațele și caii înoată este o întrebare pentru Fish, Duck, șiHorseclase, nu interfața. La fel cum schimbarea canalului este o sarcină pentru televizor. Telecomanda vă oferă pur și simplu un buton pentru asta. Cu toate acestea, o completare interesantă a apărut în Java 8 - metode implicite. De exemplu, interfața dvs. are 10 metode. 9 dintre ele au implementări diferite în clase diferite, dar una este implementată la fel pentru toate. Anterior, înainte de Java 8, metodele de interfață nu aveau nicio implementare: compilatorul dădea imediat o eroare. Acum poți face ceva de genul acesta:

public interface CanSwim {

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

   public void eat();

   public void run();
}
Folosind defaultcuvântul cheie, am creat o metodă de interfață cu o implementare implicită. Trebuie să oferim propria noastră implementare pentru alte două metode - eat()și run()- în toate clasele care implementează CanSwim. Nu trebuie să facem acest lucru cu swim()metoda: implementarea va fi aceeași în fiecare clasă. Apropo, ați întâlnit deja interfețe în sarcinile anterioare, chiar dacă nu ați observat :) Iată un exemplu viu: De ce sunt necesare interfețele în Java - 2ați lucrat cu interfețele Listși Set! Mai precis, ați lucrat cu implementările lor — ArrayList, LinkedList, HashSet, etc. Aceeași diagramă oferă în mod clar un exemplu în care o clasă implementează mai multe interfețe în același timp. De exemplu, LinkedListimplementează ListșiDequeinterfețe (coadă dublă). Sunteți familiarizat cu interfața sau, mai degrabă, cu implementarea Mapacesteia . HashMapApropo, această diagramă ilustrează o caracteristică: interfețele pot moșteni alte interfețe. Interfața SortedMapmoștenește Map, în timp ce Dequemoștenește Queue. Acest lucru este necesar dacă doriți să afișați relația dintre interfețe, unde o interfață este o versiune extinsă a alteia. Să luăm în considerare un exemplu cu Queueinterfața. Încă nu am revizuitQueues, dar este destul de simplu și funcționează ca o coadă obișnuită, sau linie, la un magazin. Puteți adăuga articole doar la sfârșitul cozii și le puteți lua doar de la început. La un moment dat, dezvoltatorii aveau nevoie de o versiune îmbunătățită a cozii pentru a adăuga și a prelua elemente la ambele capete. Așa că au creat o Dequeinterfață, care este o coadă dublă. Are toate metodele unei cozi obișnuite. La urma urmei, este părintele cozii duble, dar adaugă și metode noi.
Comentarii
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION