JavaRush /Java блог /Random UA /Кава-брейк #140. Абстрактні класи та інтерфейси в Java

Кава-брейк #140. Абстрактні класи та інтерфейси в Java

Стаття з групи Random UA
Джерело: InfoWorld Сьогодні ви дізнаєтеся, в яких випадках розробнику варто використовувати абстрактний клас, а в яких інтерфейс. Також ми визначимо різницю між цими елементами мови Java та з'ясуємо, як використовувати їх у програмах. Кава-брейк #140.  Абстрактні класи та інтерфейси в Java - 1Абстрактні класи та інтерфейси досить поширені в коді Java і навіть у Java Development Kit (JDK). Кожен із цих елементів служить своїй меті:
  • Інтерфейс - це конструкція в мові Java, яка допомагає реалізувати абстрактні методи та статичні константи.
  • Абстрактні класи схожі на звичайні класи, з тією відмінністю, що можуть включати абстрактні методи, тобто методи без тіла. Абстрактні класи не можуть бути створені.
Багато розробників вважають, що інтерфейси та абстрактні класи схожі, але насправді це не зовсім так. Давайте розглянемо основні різницю між ними.

Що таке інтерфейс

За своєю суттю, інтерфейс це контракт, тому він залежить від реалізації, яка визначає мету його створення. Інтерфейс не може використовувати змінні змінні екземпляра, він може використовувати тільки кінцеві змінні.

Коли використовувати інтерфейси

Інтерфейси дуже корисні для поділу коду та реалізації поліморфізму. Ми можемо це побачити на прикладі JDK з інтерфейсом List :
public interface List<E> extends Collection<E> {

    int size();
    boolean isEmpty();
    boolean add(E e);
    E remove(int index);
    void clear();
}
Як ви, мабуть, помітабо, цей код хоч і короткий, але дуже описовий. Ми можемо легко побачити сигнатуру методу, яка використовуватиметься для реалізації методів в інтерфейсі з використанням конкретного класу. Інтерфейс List містить контракт, який можна реалізувати класами ArrayList , Vector , LinkedList та іншими. Щоб використовувати поліморфізм, ми можемо просто оголосити тип нашої змінної за допомогою List , а потім вибрати будь-який з доступних екземплярів. Ось ще один приклад:
List list = new ArrayList();
System.out.println(list.getClass());

 List list = new LinkedList();
 System.out.println(list.getClass());
На висновку отримуємо:
class java.util.ArrayList class java.util.LinkedList
У разі методи реалізації для ArrayList , LinkedList і Vector різняться, що є відмінним сценарієм використання інтерфейсу. Якщо ви помітабо, що багато класів належать батьківському класу з тими самими діями методів, але з різною поведінкою. У таких випадках рекомендується використовувати інтерфейс. Далі розглянемо кілька варіантів застосування інтерфейсів.

Перевизначення методу інтерфейсу

Як ми вже знаємо, інтерфейс - це свого роду контракт, який має бути реалізований конкретним класом. Методи інтерфейсу неявно абстрактні та вимагають конкретної реалізації класу. Ось приклад:
public class OverridingDemo {
  public static void main(String[] args) {
    Challenger challenger = new JavaChallenger();
    challenger.doChallenge();
  }
}

interface Challenger {
  void doChallenge();
}

class JavaChallenger implements Challenger {
  @Override
  public void doChallenge() {
    System.out.println("Challenge done!");
  }
}
Висновок:
Challenge done!
Зверніть увагу, що методи інтерфейсу є неявно абстрактними. Це означає, що нам не потрібно явно оголошувати їх абстрактними.

Постійні змінні

Ще одне правило, яке слід пам'ятати, полягає в тому, що інтерфейс може містити лише постійні змінні. Ось приклад:
public class Challenger {

  int number = 7;
  String name = "Java Challenger";

}
Тут обидві змінні є неявними final і static . Це означає, що вони є константами, які не залежать від екземпляра і не можуть бути змінені. А тепер ми спробуємо змінити змінні в інтерфейсі Challenger , скажімо, ось так:
Challenger.number = 8;
Challenger.name = "Another Challenger";
Це спричинить помилку компіляції:
Cannot assign a value to final variable 'number' Cannot assign a value to final variable 'name'

Методи за замовчуванням

Коли в Java 8 з'явабося методи за промовчанням, деякі розробники думали, що вони будуть такими, як і абстрактні класи. Однак, це не так, тому що інтерфейси не можуть мати стан. Метод за замовчуванням може мати реалізацію, а абстрактні методи немає. Методи за замовчуванням є результатом інновацій з лямбда-виразами та потоками, але ми повинні використовувати їх обережно. Метод JDK, що використовує метод за замовчуванням, - це forEach() , який є частиною інтерфейсу Iterable . Замість копіювання коду в кожну реалізацію Iterable , ми можемо просто повторно використовувати метод forEach :
default void forEach(Consumer<? super T> action) {
  // Code implementation here...
Будь-яка реалізація Iterable може використовувати метод forEach() , не вимагаючи нової реалізації методу. Потім ми можемо повторно використовувати код за замовчуванням. Давайте створимо наш власний метод за промовчанням:
public class DefaultMethodExample {

  public static void main(String[] args) {
    Challenger challenger = new JavaChallenger();
    challenger.doChallenge();
  }

}

class JavaChallenger implements Challenger { }

interface Challenger {

  default void doChallenge() {
    System.out.println("Challenger doing a challenge!");
  }
}
Результат:
Challenger doing a challenge!
Щодо методів за умовчанням важливо відзначити, кожен такий метод потребує реалізації. Метод за замовчуванням може бути статичним. А тепер перейдемо до абстрактних класів.

Суть абстрактного класу

Абстрактні класи можуть мати стан зі змінними примірниками. Це означає, що змінна екземпляра може використовуватися та змінюватися. Ось приклад:
public abstract class AbstractClassMutation {

  private String name = "challenger";

  public static void main(String[] args) {
    AbstractClassMutation abstractClassMutation = new AbstractClassImpl();
    abstractClassMutation.name = "mutated challenger";
    System.out.println(abstractClassMutation.name);
  }

}

class AbstractClassImpl extends AbstractClassMutation { }
Висновок:
mutated challenger

Абстрактні методи в абстрактних класах

Як і інтерфейси, абстрактні класи може мати абстрактні методи. Абстрактний метод – це метод без тіла. На відміну від інтерфейсів, абстрактні методи в абстрактних класах мають бути явно оголошені як абстрактні. Перед вами приклад:
public abstract class AbstractMethods {

  abstract void doSomething();

}
А ось спроба оголосити метод без реалізації та без ключового слова abstract :
public abstract class AbstractMethods {
   void doSomethingElse();
}
На жаль, вона призводить до помилки компіляції:
Missing method body, або declare abstract

Коли використовувати абстрактні класи

Абстрактний клас рекомендується використовувати, коли вам потрібно реалізувати стан, що змінюється. Наприклад, Java Collections Framework включає клас AbstractList , який використовує стан змінних. У тих випадках, коли вам не потрібно підтримувати стан класу, краще використовувати інтерфейс.

Відмінності між абстрактними класами та інтерфейсами

З точки зору об'єктно-орієнтованого програмування основна відмінність між інтерфейсом та абстрактним класом полягає в тому, що інтерфейс не може мати стан, тоді як абстрактний клас може мати стан зі змінними примірниками. Інша ключова відмінність полягає в тому, що класи можуть реалізовувати більше одного інтерфейсу, але вони можуть розширювати лише абстрактний клас. Це рішення ґрунтується на тому факті, що множинне спадкування (розширення більш ніж одного класу) може призвести до взаємоблокування коду. Розробники мови Java вирішабо цього уникнути. Ще одна відмінність полягає в тому, що інтерфейси можуть бути реалізовані класами або розширені інтерфейсами, а класи можуть бути лише розширені. Важливо, що лямбда-вирази можуть використовуватися тільки з функціональним інтерфейсом (мається на увазі інтерфейс тільки з одним методом), в той час як абстрактні класи тільки з одним абстрактним методом не можуть використовувати лямбда-вирази. Ось ще кілька відмінностей між абстрактними класами та інтерфейсами. Інтерфейс:
  • Може мати лише кінцеві статичні змінні. Інтерфейс ніколи не може змінити свій власний стан.
  • Клас може реалізовувати кілька інтерфейсів.
  • Може бути реалізований за допомогою ключового слова implements. Інтерфейс може розширювати інший інтерфейс.
  • Для методів можна використовувати лише статичні кінцеві поля, параметри чи локальні змінні.
  • Тільки функціональні інтерфейси можуть використовувати лямбду в Java.
  • Не може мати архітектор.
  • Може мати абстрактні способи.
  • Може мати методи за промовчанням та статичні (представлені в Java 8).
  • Може мати окремі методи з реалізацією (представлено в Java 9).
Абстрактні класи:
  • Можуть мати будь-які екземпляри або статичні змінні, які змінюються або незмінні.
  • Клас може розширювати лише один абстрактний клас.
  • Можуть мати екземпляр змінних полів, параметрів або локальних змінних.
  • Абстрактні класи лише з одним абстрактним методом що неспроможні використовувати лямбда-выражения.
  • Можуть мати архітектор.
  • Можуть мати будь-які методи.
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ