Каковы правила обработки унаследованных омонимами методов?

Я пытаюсь понять, как 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(){}     }

Сделано это, во всяком случае, хотя я могу понять большинство случаев в моем примере, я еще не смог вывести какое-либо «общее правило».

Например, я не понимаю, почему дела2а а также3a работают по-другому, то есть почему реализация, заданная абстрактным классом, принимается, а реализация, предоставляемая интерфейсом, - нет.

Мой последний вопрос: существует ли какое-либо «общее правило»? Есть ли способ, которым я могу предсказать, как будет вести себя компилятор без необходимости запоминать каждый случай?

РЕДАКТИРОВАТЬ

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

Я думаю, что вы можете обобщить все вопросы к этим простым шагам:

Приведенный пример кода, как это

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(){}}

Ответы на вопрос(2)

Ваш ответ на вопрос