Я пытаюсь понять, как Java обрабатывает случаи неоднозначности, которые возникают, когда конкретный класс наследует (абстрактные или конкретные) методы, имеющие одно и то же имя из разных классов/интерфейсов.Каковы правила обработки унаследованных методов омонима?
Мне не удалось найти общее правило, поэтому я решил, что раз навсегда потратил на это время, используя практический подход.
я считал 8 различных случаев, сочетая
- абстрактные методы
- не абстрактные методы
- абстрактные классы
- интерфейсы
в результате этой схемы:
+-------------------------+
| INTERFACE |
+----------+--------------|
| abstract | non-abstract |
| method | method |
+-----------+--------------+----------+--------------+
| | abstract | | |
| ABSTRACT | method | 1a | 2a |
| +--------------+----------+--------------+
| CLASS | non-abstract | | |
| | method | 3a | 4a |
+-----------+--------------+----------+--------------+
| | abstract | | |
| | method | 1b | 2b |
| INTERFACE +--------------+----------+--------------+
| | non-abstract | | |
| | method | 3b | 4b |
+-----------+--------------+----------+--------------+
И здесь каждый случай реализуется и прокомментировал:
// (1a)
// A - abstract method
// I - abstract method
//
// Implementation needed to avoid compilation error:
// "The type B1 must implement the inherited abstract method A1.foo()"
//
abstract class A1{ abstract void foo(); }
interface I1{ void foo(); }
class B1 extends A1 implements I1{ public void foo(){} }
// (2a)
// A - abstract method
// I - non-abstract method
//
// Implementation needed to avoid compilation error:
// "The type B2 must implement the inherited abstract method A2.foo()"
//
abstract class A2{ abstract void foo(); }
interface I2{ default void foo(){} }
class B2 extends A2 implements I2{ public void foo(){} }
// (3a)
// A - non-abstract method
// I - abstract method
//
// Implementation not needed
//
abstract class A3{ public void foo(){} }
interface I3{ void foo(); }
class B3 extends A3 implements I3{ }
// (4a)
// A - non-abstract method
// I - non-abstract method
//
// Implementation not needed
//
abstract class A4 { public void foo(){System.out.println("A4");}}
interface I4{default void foo(){ System.out.println("I4");} }
class B4 extends A4 implements I4{ B4(){foo();} /*prints "A4"*/ }
// (1b)
// J - abstract method
// K - abstract method
//
// Implementation needed to avoid compilation error:
// "The type C1 must implement the inherited abstract method K1.foo()"
//
interface J1{ void foo(); }
interface K1{ void foo(); }
class C1 implements J1,K1{ public void foo(){} }
// (2b)
// J - abstract method
// K - non-abstract method
//
// Implementation needed to avoid compilation error:
// "The default method foo() inherited from K2 conflicts with another
// method inherited from J2"
//
interface J2{ void foo(); }
interface K2{ default void foo(){} }
class C2 implements J2,K2{ public void foo(){} }
// (3b)
// J - non-abstract method
// K - abstract method
//
// Implementation needed to avoid compilation error:
// "The default method foo() inherited from J3 conflicts with another
// method inherited from K3"
//
interface J3{ default void foo(){} }
interface K3{ void foo(); }
class C3 implements J3,K3{ public void foo(){} }
// (4b)
// J - non-abstract method
// K - non-abstract method
//
// Implementation needed to avoid compilation error:
// "Duplicate default methods named foo with the parameters() and()
// are inherited from the types K4 and J4"
//
interface J4{ default void foo(){} }
interface K4{ default void foo(){} }
class C4 implements J4,K4{ public void foo(){} }
Done, что, во всяком случае, хотя я в состоянии понять большинство случаев в моей Например, я еще не смог вывести какое-либо «общее правило».
Например, я не понимаю, почему случаи 2a и 3а работают по-разному, то есть, почему реализация определяется абстрактным классом принято в то время как реализация определяется интерфейс не является.
Мой последний вопрос: действительно ли существует «генетическое правило»? Можно ли каким-либо образом предсказать, как будет работать компилятор без необходимости запоминания каждого отдельного случая?
EDIT
Это может быть тривиальным, но я думаю, что это может быть полезным для кого-то еще подавляя свои соображения.
Я думаю, что вы можете суммировать все вопросы к этому простому шагу:
приведен пример код, как этого
abstract class A{abstract void foo();}
abstract class B extends A {protected void foo(){}}
interface I{void foo();}
interface J{default void foo(){}}
class C extends B implements I,J{}
Рассмотрят ваш класс C из всех его методов и унаследованных из них (назовем его C *)
class C* implements I,J{protected void foo(){};}
Подтвердить C * в отношении интерфейсов, которые он реализует (каждый метод неоднозначности, исходящий от интерфейсов, включая методы по умолчанию, должен быть разрешен на C путем предоставления реализации).
3a.Если C * дает действительную остановку реализации здесь
(it's not the case because method visibility cannot be reduced from public to protected)
3b. В противном случае действительная реализация необходима в C
class C extends B implements I,J{public void foo(){}}
Отформатируйте свое сообщение. У меня проблемы с текстовым графиком, который вы указали –
Я могу видеть все правильно отформатированное, на что вы ссылаетесь? –
@VinceEmigh Если вы находитесь на мобильном устройстве, они иногда используют шрифт переменной ширины для кодовых блоков. –