2015-07-24 5 views
5

Я пытаюсь понять, как 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 и работают по-разному, то есть, почему реализация определяется абстрактным классом принято в то время как реализация определяется интерфейс не является.

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

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{} 
  1. Рассмотрят ваш класс C из всех его методов и унаследованных из них (назовем его C *)

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

  2. Подтвердить 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(){}}

+0

Отформатируйте свое сообщение. У меня проблемы с текстовым графиком, который вы указали –

+1

Я могу видеть все правильно отформатированное, на что вы ссылаетесь? –

+1

@VinceEmigh Если вы находитесь на мобильном устройстве, они иногда используют шрифт переменной ширины для кодовых блоков. –

ответ

4

Состояние JLS (пункт 7 §9.4.1.3):

Когда реферат и метод по умолчанию с соответствующими подписями наследуются, мы производим ошибку.

Затем они идут, чтобы объяснить, почему они выбрали это:

В этом случае можно было бы отдать предпочтение одному или другому - возможно, мы предполагаем, что метод по умолчанию обеспечивает разумная реализация для абстрактного метода. Но это рискованно, так как помимо имени совпадения и подписи у нас нет оснований полагать, что метод по умолчанию ведет себя последовательно с контрактом абстрактного метода - метод по умолчанию, возможно, даже не существовал, когда исходный интерфейс был первоначально разработан. В этой ситуации безопаснее просить пользователя активно утверждать, что реализация по умолчанию является подходящей (через переопределяющую декларацию).

1

Общее правило заключается в том, что определения в классе имеют приоритет над методами защитника в интерфейсе. Если метод объявлен абстрактным в классе, это перекрывает существование реализации по умолчанию в интерфейсе.

Методы защитника были введены в качестве меры остановки, чтобы обеспечить возможность расширения интерфейсных интерфейсов API без нарушения совместимости. Семантика должна была быть такой, чтобы методы защитника не мешали каким-либо существующим языковым правилам.

+0

Не могли бы вы предоставить ссылки на любую ссылку? Тот факт, что классы имеют приоритет, также был моим интуитивным ответом, я хотел бы углубиться в это –