С введением методов по умолчанию в Java 8 теперь класс может наследовать один и тот же метод из нескольких мест (например, другого класса или интерфейса). Следующие правила могут быть использованы для определения того, какой метод выбран в таких случаях:
- Объявление метода класса или суперкласса всегда имеет приоритет над методом по умолчанию
- В противном случае используется метод с наиболее конкретным интерфейсом, предоставляющим настройки по умолчанию.
- Наконец, если методы одинаково специфичны, произойдет ошибка компилятора, и вам придется явно переопределить метод и указать, какой класс должен вызывать ваш класс.
Давайте рассмотрим несколько примеров и применим эти правила.
Пример 1:
Что печатает следующий код?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
public interface A { default void name() { System.out.println( "A" ); } } public interface B { default void name() { System.out.println( "B" ); } } public class C implements A { @Override public void name() { System.out.println( "C" ); } } public class D extends C implements A, B { public static void main( final String... args) { new D().name(); } } |
Ответ : C
Это связано с тем, что, как указано в правиле 1, объявление метода name()
из суперкласса C
имеет приоритет над объявлениями методов по умолчанию в A
и B
Пример 2:
Что печатает следующий код?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
public interface A { default void name() { System.out.println( "A" ); } } public interface B extends A { @Override default void name() { System.out.println( "B" ); } } public class C implements A {} public class D extends C implements A, B { public static void main( final String... args) { new D().name(); } } |
Ответ : Б
В отличие от предыдущего примера, C
не переопределяет name()
, но поскольку он реализует A
, он имеет метод по умолчанию из A
Согласно правилу 2, если в классе или суперклассе нет методов, выбирается наиболее конкретный интерфейс, предоставляющий по умолчанию. Поскольку B
расширяет A
, он более конкретен и, как результат, печатается «B».
Пример 3:
Что печатает следующий код?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
public interface A { default void name() { System.out.println( "A" ); } } public interface B { default void name() { System.out.println( "B" ); } } public class D implements A, B { public static void main( final String... args) { new D().name(); } } |
Ответ : Ошибка компилятора! Duplicate default methods named name with the parameters () and () are inherited from the types B and A
В этом примере нет более конкретного интерфейса для предоставления по умолчанию, поэтому компилятор выдает ошибку. Чтобы устранить ошибку, вам нужно явно переопределить метод в D
и указать, какое объявление метода вы хотите использовать D
Например, если вы хотите использовать B
:
1
2
3
4
5
6
|
class D implements A, B { @Override public void name() { B. super .name(); } } |
Пример 4:
Что печатает следующий код?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
public interface A { default void name() { System.out.println( "A" ); } } public interface B extends A {} public interface C extends A {} public class D implements B, C { public static void main( final String... args) { new D().name(); } } |
Ответ : А
Подинтерфейсы B
и C
не переопределяют метод, поэтому на самом деле есть только метод из A
на выбор. В качестве примечания: если бы B
или C
(но не оба) переопределили метод, то применимо правило 2. Кстати, это алмазная проблема .
Ссылка: | Java 8: Правила разрешения методов по умолчанию от нашего партнера по JCG Фахда Шарифа в блоге fahd.blog . |