logo
Методичка Java

Отличия интерфейсов от классов. Проблемы наследования интерфейсов

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

Аналогичная ситуация может возникнуть и с константами, хотя, в отличие от методов, реально этого почти никогда не происходит.

Для использования констант из разных интерфейсов решением является квалификация имени константы именем соответствующего интерфейса – всё аналогично разрешению конфликта имён в случае пакетов. Например:

public interface I1 {

Double PI=3.14;

}

public interface I2 {

Double PI=3.1415;

}

class C1 implements I1,I2 {

void m1(){

System.out.println(”I1.PI=”+ I1.PI);

System.out.println(”I2.PI=”+ I2.PI);

};

}

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

Методы, объявленные в интерфейсе, могут возбуждать проверяемые исключения. Контракты методов считаются совместимыми в случае, когда контракты отличаются только типом возбуждаемых исключительных ситуаций, причём классы этих исключений лежат в одной иерархии. При этом в классе, реализующем интерфейс, метод должен быть объявлен как возбуждающий совместимый с интерфейсом тип исключения – то есть либо такой же, как в прародительском интерфейсе, либо являющийся наследником этого типа.

Подведём некоторый итог:

В том случае, если класс A2 на уровне абстракций ведёт себя так же, как класс A1, но кроме того обладает дополнительными особенностями поведения, следует использовать наследование. То есть считать класс A2 наследником класса A1. Действует правило “A2 есть A1” (A2 is a A1).

Если для нескольких классов A1,B1,… из разных иерархий можно на уровне абстракций выделить общность поведения, следует задать интерфейс I, который описывает эти абстракции поведения. А классы задать как наследующие этот интерфейс. Таким образом, действует правило “ A1, B1,… есть I” (A1, B1,… is a I).

Множественное наследование в Java может быть двух видов:

- Только от интерфейсов, без наследования реализации.

- От класса и от интерфейсов, с наследованием реализации от прародительского класса.

Если класс-прародитель унаследовал какой-либо интерфейс, все его потомки также будут наследовать этот интерфейс.