Интерфейсы в Java 8. Статические методы, методы по умолчанию, функциональные интерфейсы

Интерфейсы в Java 8. Статические методы, методы по умолчанию, функциональные интерфейсы

Интерфейсы в Java 8 потерпели наибольшие изменения. Например, в Java 7 в интерфейсах мы могли объявлять только методы. Но начиная с Java 8 мы можем использовать в интерфейсах стандартные методы (default methods) и статические методы (static methods). Подробнее о нововведениях в интерфейсах читайте под катом

Проектирование интерфейсов — это трудная работа. Если мы хотим добавить дополнительные методы в интерфейсы, то это потребует изменения во всех классах, реализующих этот интерфейс.

Из практики: чем дольше мы поддерживаем код, тем больше интерфейс обрастает классами и наступает такой момент, что обслуживать его будет слишком накладно. Вот почему при проектировании программного обеспечения большинство программистов создают класс с базовой реализацией, а затем наследуют его и переопределяют нужные методы.

Методы по умолчанию (default methods) в интерфейсах Java 8

Для создания метода по умолчанию в интерфейсе, мы должны использовать ключевое слово default. Рассмотрим на простом примере создание метода по умолчанию.

Обратите внимание, что в представленном выше интерфейсе метод log(String str) является методом по умолчанию. Теперь, когда какой-либо класс будет реализовывать интерфейс Interface1, не является обязательным обеспечить реализацию методов по умолчанию (в нашем случае — это метод log(String str)). Функционал с методами по умолчанию очень поможет нам в будущем, когда множество классов уже будут реализовывать этот интерфейс.

А теперь давайте рассмотрим еще один интерфейс:

Мы знаем, что Java не позволяет нам наследоваться от нескольких классов, потому что это приведет к ромбовидной проблеме, где компилятор не может решить, какой метод суперкласса использовать. Теперь же с появлением методов по умолчанию, эта проблема возникнет и для интерфейсов!

Все дело в том, что если класс реализует как Interface1, так и Interface2 и не реализовывает общий метод по умолчанию, то компилятор не может решить что выбрать.

Наследование нескольких интерфейсов является неотъемлемой частью Java, поэтому теперь в Java 8 нам нужно следить за тем, чтобы эта проблема не возникала и в интерфейсах. Так что, если класс реализует оба вышеуказанных интерфейса, то он должен будет обеспечить реализацию метода log(String str), в противном случае компилятор будет бросать ошибки.

Сейчас давайте посмотрим пример класса, который реализует оба интерфейса и обеспечивает реализацию метода по умолчанию:

Коротко о главном. Методы по умолчанию в интерфейсах

  1. Методы по умолчанию помогаю реализовывать интерфейсы без страха нарушить работу других классов.
  2. Методы по умолчанию в Java 8 позволяют избежать создания служебных классов, так как все необходимые методы могут быть представлены в самих интерфейсах.
  3. Методы по умолчанию дают свободу классам выбрать метод, который нужно переопределить.
  4. Одной из основных причин внедрения методов по умолчанию является возможность коллекций (в Java 8, конечно) использовать лямбда-выражения.
  5. Если какой-либо класс в иерархии имеет метод с той же сигнатурой, то методы по умолчанию становятся неактуальными. Метод по умолчанию не может переопределить метод класса java.lang.Object. Аргументация очень проста: это потому, что объект является базовым классом для всех Java-классов. Таким образом, даже если у нас есть методы класса Object, определенные в качестве методов по умолчанию в интерфейсах, это будет бесполезно, потому что всегда будет использоваться метод класса объекта. Вот почему, чтобы избежать путаницы, мы не можем писать стандартные методы, которые переопределяли бы методы класса Object.

Статические методы в интерфейсах

Статические методы похожи на методы по умолчанию, за исключением того, что мы не можем переопределить их в классах, реализующих интерфейс. Этот функционал помогает нам избежать нежелательных результатов, которые могут появиться в дочерних классах.

Давайте посмотрим использование статических методов на простом примере:

Как видим в примере выше, мы использовали тернарный оператор для уменьшения количества кода.

А теперь давайте рассмотрим класс, который реализует интерфейс выше:

Обратите внимание, что isNull(String str) является простым методом класса, и он не переопределяет метод интерфейса. А что будет, если мы добавим аннотацию @Override к методу isNull()? Это приведет к ошибке.

Результатом выполнения приведенной выше программы является следующее:

А теперь давайте изменим в интерфейсе ключевое слово static на default. Результат выполнения представлен ниже:

Статические методы видны только для методов интерфейса. Если мы удалим метод isNull() из класса MyDataImpl, то уже не сможем использовать его для объекта MyDataImpl. Однако, как и другие статические методы, мы можем использовать имя класса для доступа к ним.

Например, так писать можно:

Коротко о главном. Статические методы в интерфейсах

  1. Статические методы в интерфейсе являются частью интерфейса, мы не можем использовать его для объектов класса реализации.
  2. Статические методы в интерфейсе хороши для обеспечения вспомогательных методов, например, проверки на null, сортировки коллекций и т.д.
  3. Статические методы в интерфейсе помогают обеспечивать безопасность, не позволяя классам, которые реализуют интерфейс, переопределить их.
  4. Мы не можем определить статические методы для методов класса Object, потому что получим ошибку компиляции: «This static method cannot hide the instance method from Object«. Это потому, что в Java так делать нельзя 🙂 . То есть Object является базовым классом для всех классов и мы не можем использовать статический метод и еще такой метод с одинаковой сигнатурой.
  5. Мы можем использовать статические методы интерфейса, чтобы не создавать вспомогательные классы, то есть переместить все статические методы в соответствующий интерфейс. Такой метод легко использовать и быстро находить.

Функциональные интерфейсы

В завершение статьи хотел бы дать краткое введение в функциональные интерфейсы.

Интерфейс с одним абстрактным методом является функциональным интерфейсом.

В Java 8 была введена новая аннотация @FunctionalInterface для обозначения интерфейса, функциональным интерфейсом. Новая аннотация @FunctionalInterface  добавляется для того, чтобы избежать случайного добавления абстрактных методов в функциональный интерфейс. Она не обязательна, но является хорошей практикой написания кода.

Функциональные интерфейсы — это долгожданная фича Java 8, потому что это позволяет нам использовать лямбда-выражения для создания экземпляра таких интерфейсов. Был добавлен новый пакет java.util.function с множеством функциональных интерфейсов.

Мы рассмотрим функциональные интерфейсы и лямбда-выражения в будущих постах. Следите за обновлениями раздела Особенности и нововведения Java 8.

2 thoughts to “Интерфейсы в Java 8. Статические методы, методы по умолчанию, функциональные интерфейсы”

  1. Здравствуйте.
    Поправьте, пожалуйста.
    После изменения в интерфейсе ключевого слово static на default, результат выполнения будет:

    1 Проверяем на null
    2 Класс MyData. Печатаем строку:
    3 Проверяем на null

Добавить комментарий для Андрей Отменить ответ

Ваш адрес email не будет опубликован. Обязательные поля помечены *