JavaRush /Java блог /Random UA /Дефолтні методи в інтерфейсах

Дефолтні методи в інтерфейсах

Стаття з групи Random UA
Кожна нова версія Java відрізняється від попередніх. Ось приклад змін із матеріалу, який ми проходабо: до Java 5 у мові не було enum'ів.
Дефолтні методи в інтерфейсах.
Так і Java 8 помітно відрізняється від Java 7. Більшість наших лекцій написані на 7 версії мови, але ігнорувати важливі нововведення ми, звичайно, не будемо. Якщо вже в цій лекції говоримо про інтерфейси, розглянемо одне оновлення - дефолтні методи в інтерфейсах . Ти вже знаєш, що інтерфейс не реалізує поведінку . Його завдання — описати, яку поведінку повинні мати всі об'єкти, що її імплементують . Але часто розробники стикалися з ситуаціями, коли реалізація якогось методу в усіх класах була однаковою. Давай розглянемо наш старий приклад із машинами:
public interface Car {

   public void gas();

   public void brake();
}
public class Sedan implements Car {

   @Override
   public void gas() {
       System.out.println("Газ!");
   }

   @Override
   public void brake() {
       System.out.println("Тормоз!");
   }
}


public class Truck implements Car {

   @Override
   public void gas() {
       System.out.println("Газ!");
   }

   @Override
   public void brake() {
       System.out.println("Тормоз!");
   }
}


public class F1Car implements Car {
   @Override
   public void gas() {
       System.out.println("Газ!");
   }

   @Override
   public void brake() {
       System.out.println("Тормоз!");
   }
}
Яка, на твою думку, головна проблема цього коду? Ти, напевно, звернув увагу, що ми написали купу однакового коду! Ця проблема поширена у програмуванні, і потрібно її уникати. Інша річ, що до виходу Java 8 особливих варіантів рішення не було. Коли з'явилася ця версія, стало можливим визначати методи за умовчанням та реалізовувати їх прямо всередині інтерфейсу! Ось як це робиться:
public interface Car {

   public default void gas() {
       System.out.println("Газ!");
   }

   public default void brake() {
       System.out.println("Тормоз!");
   }
}

public class Sedan implements Car {

}

public class Truck implements Car {

}

public class F1Car implements Car {

}
Тепер методи gas()і brake(), які для всіх машин були однаковими, винесені в інтерфейс, і код, що повторюється, не потрібен. При цьому методи доступні у кожному класі!
public class Main {

   public static void main(String[] args) {

       F1Car f1Car = new F1Car();
       Sedan sedan = new Sedan();
       Truck truck = new Truck();
       truck.gas();
       sedan.gas();
       f1Car.brake();
   }
}
А якщо є 100 класів з методом gas(), але однакову поведінку мають лише 99 із них? Це все псує і дефолтний метод у цьому випадку не підійде? Звичайно, ні :) Дефолтні методи інтерфейсів можна перевизначати.
public class UnusualCar implements Car {
   @Override
   public void gas() {
       System.out.println("Эта машина газует по-другому!");
   }

   @Override
   public void brake() {
       System.out.println("Эта машина тормозит по-другому!");
   }
}
Всі інші 99 типів машин будуть реалізовувати метод за замовчуванням, а класUnusualCar- Виняток - не зіпсує загальної картини і спокійно визначить свою поведінку. Як ти вже знаєш, у Java немає множинного успадкування. Причин цього багато, ми докладно розглянемо їх у окремій лекції. В інших мовах, наприклад, C++, воно навпаки є. Без множинного успадкування виникає серйозна проблема: в одного і того ж об'єкта може бути низка різних характеристик і «поведінок». Приклад із життя: для наших батьків ми – діти, для викладачів – студенти, для лікарів – пацієнти. У житті ми постаємо в різних ролях і, відповідно, поводимося по-різному: з викладачами ми явно розмовлятимемо не так, як із близькими друзями. Спробуємо перекласти цю ситуацію на код. Уявимо, що у нас є два класи: Ставок та Авіарій. Для ставка потрібні плаваючі птахи, а для авіарія - літаючі. Для цього ми створабо два базові класи.FlyingBirdта Waterfowl.
public class Waterfowl {
}

public class FlyingBird {
}
Відповідно, в авіарій будемо відправляти тих птахів, класи яких успадковані від FlyingBird, а в ставок - тих, хто походить від Waterfowl. Начебто все виглядає просто. Але що робитимемо, якщо треба буде кудись визначити качку? Вона і плаває, і літає. А множинного успадкування ми не маємо. На щастя, Java передбачена множинна реалізація інтерфейсів. Якщо успадковуватись від кількох батьків клас не може, реалізовувати кілька інтерфейсів – запитто! Наша качка може бути і літаючою, і плаваючою:) Досить зробити FlyingBirdі Waterfowlне класами, а інтерфейсами, щоб досягти потрібного результату.
public class Duck implements FlyingBird, Waterfowl {

   //методы обоих интерфейсов легко объединяются в одном классе

   @Override
   public void fly() {
       System.out.println("Летим!");
   }

   @Override
   public void swim() {

       System.out.println("Плывем!");
   }
}
Завдяки цьому наша програма зберігає гнучке управління класами, а у поєднанні з реалізацією дефолтних методів наші можливості визначати поведінку об'єктів стають практично безмежними! :)
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ