- PVSM.RU - https://www.pvsm.ru -
Интерфейс в Java сильно эволюционировал за прошедшие годы. Давайте рассмотрим, какие изменения произошли в процессе его развития.
Интерфейсы в Java 1.0 были достаточно простыми по сравнению с тем, какие они сейчас. Они могли содержать лишь два типа элементов: константы и публичные абстрактные методы.
Интерфейсы могут содержать поля, так же как и обычные классы, но с несколькими отличиями:
public interface MyInterface {
int MY_CONSTANT = 9;
}
Даже несмотря на то, что явно это не задано, поле MY_CONSTANT считается публичной статической финальной константой. Вы можете добавить эти модификаторы, но делать это не обязательно.
Наиболее важными элементами интерфейса являются его методы. Методы интерфейса также отличаются от методов обычного класса:
public interface MyInterface {
int doSomething();
String doSomethingCompletelyDifferent();
}
Java 1.1 представила концепцию классов, которые можно размещать внутри других классов. Такие классы бывают двух видов: статические и нестатические. Интерфейсы так же могут содержать внутри себя другие интерфейсы и классы.
Даже если это не задано явно, такие интерфейсы и классы считаются публичными и статическими.
public interface MyInterface {
class MyClass {
//...
}
interface MyOtherInterface {
//...
}
}
В Java 5 были введены ещё два типа: Перечисления и Аннотации. Они также могут быть помещены внутрь интерфейсов.
public interface MyInterface {
enum MyEnum {
FOO, BAR;
}
@interface MyAnnotation {
//...
}
}
Java 5 ввела концепцию «дженериков» — обобщенных типов. Вкратце: «дженерики» позволяют вам использовать обобщенный тип вместо указания конкретного типа. Таким образом, вы можете писать код, который работает с различным количеством типов, не жертвуя при этом безопасностью и не предоставляя отдельную реализацию для каждого типа.
В интерфейсах, начиная с Java 5, вы можете определить обобщенный тип, а затем использовать его в качестве типа возвращаемого значения метода или в качестве типа аргумента метода.
Интерфейс Box работает независимо от того, используете ли вы его для хранения объектов типа String, Integer, List, Shoe или каких-либо других.
interface Box<T> {
void insert(T item);
}
class ShoeBox implements Box<Shoe> {
public void insert(Shoe item) {
//...
}
}
Начиная с Java 8, вы можете включать в интерфейсы статические методы. Данный подход изменил привычный для нас способ работы интерфейса. Они теперь работают совсем не так, как работали до Java 8. Первоначально все методы в интерфейсах были абстрактными. Это означало, что интерфейс предоставлял лишь сигнатуру, но не реализацию. Реализация оставалась за классами, реализующими ваш интерфейс.
При использовании статических методов в интерфейсах вам нужно также предоставить реализацию тела метода. Чтобы задействовать в интерфейсе такой метод, просто используйте ключевое слово static. Статические методы считаются публичными по умолчанию.
public interface MyInterface {
// This works
static int foo() {
return 0;
}
// This does not work,
// static methods in interfaces need body
static int bar();
}
В отличие от обычных статических методов, статические методы в интерфейсах не наследуются. Это означает, что если вы хотите вызвать такой метод, вы должны вызвать его напрямую из интерфейса, а не из реализующего его класса.
MyInterface.staticMethod();
Это поведение очень полезно для избежания проблем при множественном наследовании. Представьте, что у вас есть класс, реализующий два интерфейса. У каждого из интерфейсов есть статический метод с одинаковым именем и сигнатурой. Какой из них должен быть использован в первую очередь?
Представьте, что у вас есть интерфейс и целый набор вспомогательных методов, которые работают с классами, реализующими этот интерфейс.
Традиционно существовал подход в использовании класса-компаньона. В дополнение к интерфейсу создавался утилитный класс с очень похожим именем, содержащий статические методы, принадлежащие интерфейсу.
Вы можете найти примеры использования данного подхода прямо в JDK: интерфейс java.util.Collection и сопутствующий ему утилитный класс java.util.Collections.
Со статическими методами в интерфейсах этот подход больше не актуален, не нужен и не рекомендован. Теперь вы можете иметь все в одном месте.
Методы по умолчанию похожи на статические методы тем, что для них вы также должны предоставлять тело. Чтобы объявить метод по умолчанию, просто используйте ключевое слово default.
public interface MyInterface {
default int doSomething() {
return 0;
}
}
В отличие от статических методов, методы по умолчанию наследуются классами, реализующими интерфейс. Что важно, такие классы могут при необходимости переопределять их поведение.
Хотя есть одно исключение. В интерфейсе не может быть методов по умолчанию с такой же сигнатурой, как у методов toString, equals и hashCode класса Object. Взгляните на ответ Брайана Гетца, чтобы понять обоснованность такого решения: Allow default methods to override Object's methods. [1]
Идея с реализацией методов прямо в интерфейсе выглядит не совсем правильной. Так почему в первую очередь была введена эта функциональность?
У интерфейсов есть одна проблема. Как только вы предоставите свой API другим людям, он навсегда «окаменеет» (его нельзя будет изменить безболезненно).
По традиции, Java очень серьезно относится к обратной совместимости. Методы по умолчанию предоставляют способ расширить существующие интерфейсы новыми методами. Наиболее важно то, что методы по умолчанию уже предоставляют определенную реализацию. Это означает, что классам, реализующим ваш интерфейс, не нужно реализовывать какие-либо новые методы. Но, если в этом будет необходимость, методы по умолчанию можно будет переопределить в любое время, если их реализация перестанет подходить. Таким образом, вкратце, вы можете предоставить новую функциональность существующим классам, реализующим ваш интерфейс, сохраняя при этом совместимость.
Давайте представим, что у нас есть класс, реализующий два интерфейса. У этих интерфейсов есть метод по умолчанию с одинаковыми именем и сигнатурой.
interface A {
default int doSomething() {
return 0;
}
}
interface B {
default int doSomething() {
return 42;
}
}
class MyClass implements A, B {
}
Теперь один и тот же метод по умолчанию с одной и той же сигнатурой унаследован от двух разных интерфейсов. У каждого интерфейса своя реализация этого метода.
Итак, как наш класс узнает, какую из двух различных реализаций использовать?
Он и не узнает. Приведенный выше код приведет к ошибке компиляции. Если вам требуется заставить его работать, то для этого необходимо переопределить конфликтный метод в вашем классе.
interface A {
default int doSomething() {
return 0;
}
}
interface B {
default int doSomething() {
return 42;
}
}
class MyClass implements A, B {
// Without this the compilation fails
@Override
public int doSomething() {
return 256;
}
}
С появлением Java 8 и введением методов по умолчанию и статических методов, у интерфейсов появилась возможность содержать не только сигнатуры методов, но и их реализации. При написании таких реализаций рекомендуется разделять сложные методы на более простые. Такой код легче переиспользовать, поддерживать и понимать.
Для такой цели вы бы использовали приватные методы, поскольку они могут содержать все детали реализации, которые не должны быть видимы и использованы извне.
К сожалению в Java 8 интерфейс не может содержать приватные методы. Это означает, что вы можете использовать:
К счастью, начиная с Java 9, вы можете использовать приватные методы в интерфейсах [2]. У них есть следующие особенности:
public interface MyInterface {
private static int staticMethod() {
return 42;
}
private int nonStaticMethod() {
return 0;
}
}
Ниже представлен хронологический перечень изменений по версиям Java:
Вложенные классы
Вложенные интерфейсы
Обобщенные типы
Вложенные перечисления
Вложенные аннотации
Методы по умолчанию
Статические методы
Приватные методы
Автор: topjava
Источник [3]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/java/341683
Ссылки в тексте:
[1] Allow default methods to override Object's methods.: http://mail.openjdk.java.net/pipermail/lambda-dev/2013-March/008435.html
[2] Java 9, вы можете использовать приватные методы в интерфейсах: http://openjdk.java.net/jeps/213
[3] Источник: https://habr.com/ru/post/482498/?utm_campaign=482498&utm_source=habrahabr&utm_medium=rss
Нажмите здесь для печати.