Кожна нова версія Java відрізняється від попередніх. Ось приклад змін із матеріалу, який ми проходили: до Java 5 у мові не було
Так і Java 8 помітно відрізняється від Java 7. Ігнорувати важливі нововведення ми, звісно, не будемо. Якщо вже в цій лекції говоримо про інтерфейси, розглянемо одне оновлення —
дефолтні методи в інтерфейсах. Ти вже знаєш, що
інтерфейс не реалізує поведінку. Його завдання —
описати, яку поведінку повинні мати всі об'єкти, що його імплементують. Але часто розробники стикалися з ситуаціями, коли реалізація якогось методу в усіх класах була однаковою. Давай розглянемо наш старий приклад з авто:
enum’ів.
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("Пливемо!");
}
}
Завдяки цьому наша програма зберігає гнучке управління класами, а у поєднанні з реалізацією дефолтних методів наші можливості визначати поведінку об'єктів стають практично безмежними! :)
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ