Каковы различия между абстрактными классами и интерфейсами в Java 8?

в Java есть тонкое, но важное различие между абстрактными классами и интерфейсами: реализации по умолчанию. Абстрактные классы могли иметь их, интерфейсы-нет. Хотя Java 8 вводит реализации по умолчанию для интерфейсов, это означает, что это больше не критическое различие между интерфейсом и абстрактным классом.

Так что?

насколько я могу судить, единственная оставшаяся разница (кроме, возможно, некоторых под Hood efficiency stuff) заключается в том, что абстрактные классы следуют традиционному однонаследованию Java, тогда как интерфейсы могут иметь множественное наследование (или множественную реализацию, если хотите). Это приводит меня к другому вопросу -

Как новые интерфейсы Java 8 избегают проблема?

5 ответов


интерфейсы не могут иметь состояние, связанное с ними.

абстрактные классы могут иметь состояние, связанное с ними.

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

и, наконец, в случае возникновения проблемы с алмазом компилятор предупредит вас и вы нужно выбрать, какой интерфейс вы хотите реализовать.

чтобы показать больше о проблеме алмаза, рассмотрим следующий код:

interface A {
    void method();
}

interface B extends A {
    @Override
    default void method() {
        System.out.println("B");
    }
}

interface C extends A { 
    @Override
    default void method() {
        System.out.println("C");
    }
}

interface D extends B, C {

}

здесь я получаю ошибку компилятора на interface D extends B, C, что:

interface D inherits unrelated defaults for method() form types B and C

исправления:

interface D extends B, C {
    @Override
    default void method() {
        B.super.method();
    }
}

на случай, если я захочу наследовать method() С B.
То же самое относится и к if D были class.

показать еще больше о разнице между интерфейсами и абстрактными классами в Java 8, рассмотрим следующее Team:

interface Player {

}

interface Team {
    void addPlayer(Player player);
}

теоретически вы можете предоставить реализацию по умолчанию addPlayer такие, что вы можете добавить игроков, например, в список игроков.
Но подождите...?
Как хранить список игроков?
Ответ в том, что вы не можете сделать это в интерфейсе, даже если у вас есть реализации по умолчанию.


были некоторые очень подробные ответы, но они, похоже, упускают один момент, который я, по крайней мере, считаю одним из очень немногих оправданий наличия абстрактных классов на всех:

абстрактные классы могут иметь защищенный члены (и члены с видимостью по умолчанию). Методы в интерфейсах неявно общественные.


определение проблема - это расплывчато. Существуют всевозможные проблемы, которые могут возникнуть при множественном наследовании. К счастью, большинство из них можно легко обнаружить во время компиляции, и языки программирования поддерживают простые решения для решения этих проблем. Большинство из этих проблем даже не специфичны для проблема. Например, конфликтующие определения методов также могут возникать без алмазов:

interface Bar {
    default int test() { return 42; }
}
interface Baz {
    default int test() { return 6 * 9; }
}
class Foo implements Bar, Baz { }

конкретная проблема с алмазов вопрос включительно и эксклюзивные. Если у вас есть иерархия типов, где B и C выводим из A и D происходит от B и C, тогда вопрос:

  • и D a B *и*C (т. е. одного типа A), или
  • и D a B * или * a C (т. е. два типа A).

Ну, в Java 8 вида A должен быть интерфейс. Так что это не имеет значения, потому что интерфейсы нет государства. Не имеет значения, что интерфейсы определить методы по умолчанию, так как они также не имеют состояния. Они могут вызывать методы, которые имеют прямой доступ к государству. Однако эти методы всегда реализуются на основе одного наследования.


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

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


Java 8, хотя вводит реализации по умолчанию для интерфейсов, что означает, что это больше не критическая разница между интерфейсом и абстрактным классом.

все еще есть несколько более важных различий. См. этот пост:

интерфейс с методами по умолчанию против абстрактного класса в Java 8

как новые интерфейсы Java 8 избегают проблемы с алмазами?

случае 1: вы реализуете два интерфейса, которые имеют такую же default метод, вы должны разрешить конфликт в вашей реализации class

interface interfaceA{
    default public void foo(){
        System.out.println("InterfaceA foo");
    }
}
interface interfaceB{
    default public void foo(){
        System.out.println("InterfaceB foo");
    }
}
public class DiamondExample implements interfaceA,interfaceB{
    public void foo(){
        interfaceA.super.foo();
    }
    public static void main(String args[]){
        new DiamondExample().foo();
    }
} 

выше примера производят ниже outout:

InterfaceA foo

Пример 2: вы расширяете базовый класс и реализуете интерфейс с методом по умолчанию. Компилятор решает проблему diamond для вас, и вам не нужно ее решать, как в первом примере.

interface interfaceA{
    default public void foo(){
        System.out.println("InterfaceA foo");
    }
}

class DiamondBase {
    public void foo(){
        System.out.println("Diamond base foo");
    }
}

public class DiamondExample extends DiamondBase implements interfaceA{

    public static void main(String args[]){
        new DiamondExample().foo();
    }
}

над примером произведите под выходом:

Diamond base foo