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("Плывем!");
   }
}
Благодаря этому наша программа сохраняет гибкое управление классами, а в сочетании с реализацией дефолтных методов наши возможности определять поведение объектов становятся практически безграничными! :)
Комментарии (189)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Ni “ST” Su Уровень 26
21 ноября 2023
🤓
Denis Gritsay Уровень 25
18 ноября 2023
👍
Максим Li Уровень 36
12 ноября 2023
Ок
Anatoly Уровень 22
30 октября 2023
круто
Arturs M Уровень 26
22 октября 2023
Спасибо. Теперь я знаю, что такое авиарий 🤓
Denis Gritsay Уровень 25
21 октября 2023
отличная статья, кратко и понятно
Islam Yunusov Уровень 25
20 сентября 2023
Пример с машинами не самый удачный, на мой взгляд. Тут подошёл бы больше абстрактный класс, ибо он описывает концепт будущего класса, его состояние и поведение. Это можно увидеть из этой статьи.
chess.rekrut Уровень 25
21 августа 2023
easy
Rustam Уровень 35 Student
27 июля 2023
Очень хорошо донесли идею!
Alexander Rozenberg Уровень 30
26 июля 2023
fine