CodeGym /Java блог /Случаен /Защо имаме нужда от интерфейси в Java
John Squirrels
Ниво
San Francisco

Защо имаме нужда от интерфейси в Java

Публикувано в групата
здрасти Днес ще говорим за важна концепция в Java: интерфейси. Думата вероятно ви е позната. Например повечето компютърни програми и игри имат интерфейси. В широк смисъл интерфейсът е вид „дистанционно управление“, което свързва две взаимодействащи страни. Прост пример за интерфейс в ежедневието е дистанционното управление на телевизора. Той свързва два обекта - човек и телевизор - и изпълнява различни задачи: усилване or намаляване на звука, превключване на канали и включване or изключване на телевизора. Едната страна (човекът) трябва да получи достъп до интерфейса (натисне бутон на дистанционното управление), за да накара втората страна да извърши действието. Например, за да накарате телевизора да премине към следващия канал. Нещо повече, потребителят не Не трябва да знаете How е организиран телевизорът or How вътрешно се изпълнява процесът на смяна на канала. Единственото нещо, до което потребителят има достъп, е интерфейсът. Основната цел е да получите желания резултат. Какво общо има това с програмирането и Java? Всичко :) Създаването на интерфейс е много подобно на създаването на обикновен клас, но instead of това се използва думатаклас , посочваме думата интерфейс . Нека да разгледаме най-простия Java интерфейс, да видим How работи и защо ще ни трябва:

public interface CanSwim {

     public void swim();
}
Създадохме интерфейс CanSwim . Това е малко като нашето дистанционно управление, но с един „бутон“: методът swim() . Но How да използваме това дистанционно управление? За да направим това, трябва да внедрим метод, т.е. нашия бутон за дистанционно управление. За да използвате интерфейс, някои класове в нашата програма трябва да имплементират неговите методи. Нека измислим клас, чиито обекти „могат да плуват“. Например клас 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 е „асоцииран“ с интерфейса CanSwim чрез ключовата дума implements . Може би си спомняте, че използвахме подобен механизъм, за да асоциираме два класа чрез наследяване, но в този случай използвахме думата extends. пълна яснота, можем да преведем „ публичен клас Duck прилага CanSwim “ буквално като: „Общественият клас Duck внедрява интерфейса CanSwim “. Това означава, че клас, свързан с интерфейс, трябва да имплементира всички негови методи. Забележка: нашият Duckклас, точно като интерфейсът CanSwim, има swim()метод и съдържа няHowва логика. Това е задължително изискване. Ако просто пишемpublic class Duck implements CanSwimбез да създадем swim()метод в Duckкласа, компилаторът ще ни даде грешка: Duck не е абстрактен и не замества абстрактния метод swim() в CanSwim Защо? Защо това се случва? Ако обясним грешката с помощта на примера с телевизора, би било все едно да подадете на някого дистанционно управление за телевизор с бутон „смяна на канал“, който не може да сменя каналите. Можете да натискате бутона колкото искате, но няма да работи. Дистанционното управление не сменя каналите само: то само изпраща сигнал до телевизора, който осъществява сложния процес на смяна на канала. Така е и с нашата патица: тя трябва да знае How да плува, за да може да бъде извикана чрез CanSwimинтерфейса. Ако не знае How,CanSwimинтерфейсът не свързва двете страни — човека и програмата. Лицето няма да може да използва swim()метода за извършване на Duckплуване вътре в програмата. Сега разбирате по-ясно за Howво са предназначени интерфейсите. Интерфейсът описва поведението, което класовете, изпълняващи интерфейса, трябва да имат. „Поведение“ е колекция от методи. Ако искаме да създадем няколко месинджъра, най-лесно е да създадем интерфейс Messenger. От Howво се нуждае всеки месинджър? На основно ниво те трябва да могат да получават и изпращат съобщения.

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!");
     }
}
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!");
     }
}
Вайбър:

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;
}
Създадохме три полета, но клиентът може да има само един месинджър. Просто не знаем кой. Така че трябва да добавим всяка възможност към класа, за да можем да комуникираме с клиента. Оказва се, че един or два от тях винаги ще бъдат nullнапълно ненужни на програмата. Вместо това е по-добре да използвате нашия интерфейс:

public class Client {

    private Messenger messenger;
}
Това е пример за разхлабена връзка! Вместо да посочим конкретен клас месинджър в Clientкласа, ние просто посочваме, че клиентът има месинджър. Кое точно ще се определи по време на изпълнение на програмата. Но защо се нуждаем от интерфейси за това? Защо изобщо са добавени към езика? Това е добър въпрос — и точен! Не можем ли да постигнем същия резултат, използвайки обикновено наследяване? Класът Messengerкато родител и Viber, Telegram, и WhatsAppкато деца. Наистина, това е възможно. Но има една пречка. Както вече знаете, Java няма множествено наследяване. Но има поддръжка за множество интерфейси. Един клас може да реализира толкова интерфейси, колкото искате. Представете си, че имаме Smartphoneклас, който има такъвAppполе, което представлява приложение, инсталирано на смартфона.

public class Smartphone {

    private App app;
}
Разбира се, приложението и месинджърът са подобни, но все пак са различни неща. Може да има мобилни и настолни версии на месинджър, но приложението конкретно представлява мобилно приложение. Ето Howва е сделката — ако използвахме наследяване, нямаше да можем да добавим Telegramобект към Smartphoneкласа. В крайна сметка Telegramкласът не може едновременно да наследява Appи Messenger! И вече го направихме наследник Messengerи го добавихме към Clientкласа. Но Telegramкласът може лесно да реализира и двата интерфейса! Съответно, можем да дадем на Clientкласа Telegramобект като Messenger, и можем да го дадем на Smartphoneкласа като App. Ето How да направите това:

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класа Howто искаме. На някои места той действа като App. На други места той действа като Messenger. Със сигурност вече сте забелязали, че методите на интерфейса винаги са „празни“, т.е. нямат имплементация. Причината за това е проста: интерфейсът описва поведението, но не го прилага. „Всички обекти, които имплементират CanSwimинтерфейса, трябва да могат да плуват“: това е всичко, което интерфейсът ни казва. Конкретният начин, по който рибите, патиците и конете плуват, е въпрос за Fish, Duck, иHorseкласове, а не интерфейса. Точно Howто смяната на канала е задача за телевизора. Дистанционното просто ви дава бутон за това. В Java 8 обаче се появи интересно допълнение — методи по подразбиране. Например вашият интерфейс има 10 метода. 9 от тях имат различни имплементации в различни класове, но един е имплементиран еднакво за всички. Преди това, преди Java 8, методите на интерфейса нямаха ниHowва реализация: компилаторът веднага даде грешка. Сега можете да направите нещо подобно:

public interface CanSwim {

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

   public void eat();

   public void run();
}
Използвайки defaultключовата дума, създадохме интерфейсен метод с имплементация по подразбиране. Трябва да предоставим наша собствена реализация за два други метода — eat()и run()— във всички класове, които имплементират CanSwim. Не е необходимо да правим това с swim()метода: имплементацията ще бъде еднаква във всеки клас. Между другото, вече сте срещали интерфейси в минали задачи, дори и да не сте забелязали :) Ето един ярък пример: Защо са необходими интерфейси в Java - 2Работor сте с интерфейси Listи Set! По-точно, вие сте работor с техните имплементации — ArrayList, LinkedList, HashSetи т.н. Същата диаграма ясно дава пример, когато един клас имплементира множество интерфейси едновременно. Например, LinkedListприлага ListиDeque(двустранна опашка) интерфейси. Вие сте запознати с Mapинтерфейса or по-скоро с неговата HashMapреализация. Между другото, тази диаграма илюстрира функция: интерфейсите могат да наследяват други интерфейси. Интерфейсът SortedMapнаследява Map, докато Dequeнаследява Queue. Това е необходимо, ако искате да покажете връзката между интерфейсите, където един интерфейс е разширена version на друг. Нека разгледаме пример с Queueинтерфейса. Все още не сме прегледалиQueues, но е доста просто и работи като обикновена опашка or линия в магазин. Можете да добавяте елементи само в края на опашката и можете да ги вземете само от началото. В един момент разработчиците се нуждаеха от подобрена version на опашката, за да добавят и вземат елементи в двата края. Така те създадоха Dequeинтерфейс, който представлява опашка с два края. Има всички методи на обикновена опашка. В крайна сметка това е родителят на опашката с двоен край, но също така добавя нови методи.
Коментари
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION