JavaRush/Java блог/Java Developer/Дефолтные методы в интерфейсах
Автор
Владимир Портянко
Java-разработчик в Playtika

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

Статья из группы Java Developer
участников
Каждая новая версия Java отличается от предыдущих. Вот пример изменений из материала, который мы проходили: до Java 5 в языке не было enum’ов.
Дефолтные методы в интерфейсах - 1
Так и 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("Плывем!");
   }
}
Благодаря этому наша программа сохраняет гибкое управление классами, а в сочетании с реализацией дефолтных методов наши возможности определять поведение объектов становятся практически безграничными! :)
Комментарии (190)
  • популярные
  • новые
  • старые
Для того, чтобы оставить комментарий Вы должны авторизоваться
Антон Бушенев
Уровень 21
13 января, 05:37
не знал такого понятия как авиарий, спасибо :)
Pavel Fetisov
Уровень 26
15 февраля, 19:52
Ага, в этой статье это, пожалуй, единственное новое, что я узнал. В остальном - просто повторил, что тоже неплохо.
Ni “ST” Su
Уровень 26
21 ноября 2023, 14:10
🤓
Denis Gritsay
Уровень 37
18 ноября 2023, 16:43
👍
Максим Li Java Developer
12 ноября 2023, 21:48
Ок
Anatoly Enterprise Java Developer
30 октября 2023, 14:32
круто
Arturs M
Уровень 26
22 октября 2023, 10:58
Спасибо. Теперь я знаю, что такое авиарий 🤓
Denis Gritsay
Уровень 37
21 октября 2023, 22:26
отличная статья, кратко и понятно
Islam Yunusov
Уровень 31
20 сентября 2023, 10:31
Пример с машинами не самый удачный, на мой взгляд. Тут подошёл бы больше абстрактный класс, ибо он описывает концепт будущего класса, его состояние и поведение. Это можно увидеть из этой статьи.
chess.rekrut
Уровень 25
21 августа 2023, 15:02
easy
Rustam
Уровень 35
Student
27 июля 2023, 07:42
Очень хорошо донесли идею!