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