¿Cuáles son las reglas para manejar métodos heredados de homónimo?

Estoy tratando de entender cómo Java maneja los casos de ambigüedad que surgen cuando una clase concreta hereda métodos (abstractos o concretos) que tienen el mismo nombre de diferentes clases / interfaces.

No he podido encontrar una regla general, es por eso que decidí, de una vez por todas, dedicar algo de tiempo a esto mediante un enfoque práctico.

Considere 8 casos diferentes, combinando

métodos abstractosmétodos no abstractosclases abstractasinterfaces

resultando en este esquema:

                           +-------------------------+
                           |       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      |
+-----------+--------------+----------+--------------+

Y aquí cada caso se implementa y comenta:

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

Hecho eso, de todos modos, aunque puedo entender la mayoría de los casos en mi ejemplo, todavía no he podido inferir ninguna "regla general".

Por ejemplo, no entiendo por qué los casos2a y3a funcionan de manera diferente, es decir, por qué una implementación dada por la clase abstracta es aceptada mientras que una implementación dada por la interfaz no lo es.

Mi última pregunta es: ¿existe realmente alguna "regla genaral"? ¿Hay alguna forma de predecir cómo se comportará el compilador sin tener que memorizar cada caso?

EDITAR

Podría ser trivial, pero creo que puede ser útil para que alguien menospreciando mis consideraciones.

Creo que puede resumir toda la pregunta en estos simples pasos:

Dado un código de ejemplo como este

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

Considere su clase C compuesta de todos sus métodos y heredados (llámela C *)

class C* implements I,J{protected void foo(){};}

Valide C * contra las interfaces que implementa (cada ambigüedad de método proveniente de las interfaces, incluidos los métodos predeterminados, debe resolverse en C mediante una implementación).

3a. Si C * da una implementación válida, deténgase aquí

(it's not the case because method visibility cannot be reduced from public to protected)

3b. De lo contrario, se necesita una implementación válida en C

class C extends B implements I,J{public void foo(){}}

Respuestas a la pregunta(2)

Su respuesta a la pregunta