Какой метод используется по умолчанию?
После выпуска Java 8 в интерфейс можно добавлять новые методы, но интерфейс по-прежнему останется совместимым со своим классом реализации. Это важно, поскольку разрабатываемая вами библиотека может широко использоваться несколькими разработчиками. До Java 8, после публикации интерфейса в библиотеке классов, если к интерфейсу добавлялся новый метод, приложения, реализующие этот интерфейс, подвергались опасности сбоя при использовании новой версии интерфейса.
С Java 8 нет такой опасности? Ответ — нет.
Добавление методов по умолчанию в интерфейс может сделать некоторые классы реализации недоступными.
Во-первых, давайте посмотрим на детали метода по умолчанию.
В Java 8 можно реализовать методы в интерфейсах (статические методы в Java 8 также можно реализовать в интерфейсах, но это уже другая тема). Метод, реализованный в интерфейсе, называется методом по умолчанию, который идентифицируется ключевым словом default в качестве модификатора. Когда класс реализует интерфейс, он может реализовывать методы, которые уже реализованы в интерфейсе, но это не обязательно. Этот класс унаследует метод по умолчанию. Вот почему при изменении интерфейса нет необходимости менять класс реализации.
А как насчет множественного наследства?
Когда класс реализует более одного (например, двух) интерфейсов, и эти интерфейсы имеют один и тот же метод по умолчанию, все становится очень сложным. Какой метод по умолчанию наследует класс? Ни один! В этом случае сам класс (либо непосредственно, либо класс выше по дереву наследования) должен реализовать метод по умолчанию.
То же самое верно, когда один интерфейс реализует метод по умолчанию, а другой интерфейс объявляет метод по умолчанию как абстрактный. Java 8 пытается избежать двусмысленности и сохранить строгость. Если метод объявлен в нескольких интерфейсах, ни одна из реализаций по умолчанию не будет унаследована, и вы получите ошибку времени компиляции.
Однако если вы скомпилировали свой класс, ошибок во время компиляции не будет. На данный момент Java 8 несовместима. На это есть свои причины, и причины разные. Я не хочу здесь это подробно объяснять или подробно обсуждать (потому что: версия вышла, время обсуждения слишком велико, а на этой платформе никогда не было). такое обсуждение).
1. Предположим, у вас есть два интерфейса и класс реализации.
2. Один из интерфейсов реализует метод по умолчанию m().
3. Скомпилируйте класс интерфейса и реализации вместе.
4. Измените интерфейс, не содержащий метода m(), и объявите метод m() абстрактным.
5. Перекомпилируйте модифицированный интерфейс отдельно.
6. Запустите класс реализации.
1. Измените интерфейс, содержащий абстрактный метод m(), и создайте реализацию по умолчанию.
2. Скомпилируйте модифицированный интерфейс
3. Запустить класс: Не удалось.
Когда два интерфейса предоставляют реализацию по умолчанию для одного и того же метода, этот метод не может быть вызван, если класс реализации также не реализует метод по умолчанию (либо напрямую, либо с помощью класса более высокого уровня в дереве наследования).
Пример кода:
Чтобы продемонстрировать приведенный выше пример, я создал тестовый каталог для C.java, в котором есть 3 подкаталога для хранения I1.java и I2.java. В тестовом каталоге находится исходный код C.java класса C. Базовый каталог содержит версию интерфейса, которую можно скомпилировать и запустить. I1 содержит метод m() с реализацией по умолчанию, а I2 не содержит никаких методов.
Класс реализации содержит основной метод, поэтому мы можем выполнять его в наших тестах. Он проверит наличие параметров командной строки, чтобы мы могли легко выполнять тесты, вызывая m() и не вызывая m().
Скопируйте код кода следующим образом:
~/github/test$ cat C.java
публичный класс C реализует I1, I2 {
public static void main(String[] args) {
C c = новый C();
если(args.length == 0){
см();
}
}
}
~/github/test$ cat base/I1.java
общедоступный интерфейс I1 {
по умолчанию void m(){
System.out.println("Привет, интерфейс 1");
}
}
~/github/test$ cat base/I2.java
общедоступный интерфейс I2 {
}
Используйте следующую командную строку для компиляции и запуска:
Скопируйте код следующим образом: ~/github/test$ javac -cp .:base C.java
~/github/test$ java -cp .:base C
привет интерфейс 1
Совместимый каталог содержит интерфейс I2 с абстрактным методом m() и немодифицированный интерфейс I1.
Скопируйте код следующим образом: ~/github/test$ catсовместимый/I2.java
общедоступный интерфейс I2 {
недействительный м ();
}
Это нельзя использовать для компиляции класса C:
Скопируйте код следующим образом: ~/github/test$ javac -cp .:совместимый C.java
C.java:1: ошибка: C не является абстрактным и не переопределяет абстрактный метод m() в I2.
публичный класс C реализует I1, I2 {
^
1 ошибка
Сообщение об ошибке очень точное. Поскольку у нас есть C.class, полученный в предыдущей компиляции, если мы скомпилируем интерфейсы в совместимом каталоге, мы все равно получим два интерфейса, которые могут запускать класс реализации:
Скопируйте код кода следующим образом:
~/github/test$ Javac-совместимый/I*.java
~/github/test$ java -cp .:совместимый C
привет интерфейс 1
Третий каталог, называемый неправильно, содержит интерфейс I2, который также определяет метод m():
Скопируйте код кода следующим образом:
~/github/test$ cat неправильно/I2.java
общедоступный интерфейс I2 {
по умолчанию void m(){
System.out.println("привет, интерфейс 2");
}
}
Нам следует потрудиться его скомпилировать. Хотя метод m() определен дважды, класс реализации все равно может работать, пока он не вызывает метод, определенный несколько раз. Однако, пока мы вызываем метод m(), он немедленно завершится ошибкой. Вот параметры командной строки, которые мы используем:
Скопируйте код кода следующим образом:
~/github/test$ Javac неправильный/*.java
~/github/test$ java -cp .:неправильный C
Исключение в потоке «основной» java.lang.InсовместимыйClassChangeError: конфликт
методы по умолчанию: I1.m I2.m
в см(C.java)
в C.main(C.java:5)
~/github/test$ java -cp .:неправильный C x
~/github/test$
в заключение
Когда вы портируете библиотеку классов, которая добавляет реализацию по умолчанию к интерфейсу в среду Java 8, проблем обычно не возникает. По крайней мере, так думали разработчики библиотеки классов Java8, когда добавляли методы по умолчанию в классы-коллекции. Приложения, использующие вашу библиотеку, по-прежнему полагаются на библиотеки Java 7, у которых нет методов по умолчанию. При использовании и изменении нескольких различных библиотек классов существует небольшая вероятность возникновения конфликтов. Как этого можно избежать?
Создайте свою библиотеку классов, как и раньше. Не относитесь к этому легкомысленно, полагаясь на метод по умолчанию. Не используйте в качестве крайней меры. Выбирайте имена методов с умом, чтобы избежать конфликтов с другими интерфейсами. Мы научимся использовать эту функцию для разработки программ на Java.