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


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

Так что?

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

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

5 63

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 должен быть интерфейс. Так что это не имеет никакого значения, потому что интерфейсы нет государства. Это не имеет значения, что интерфейсы определить методы по умолчанию, так как у них тоже нет государства. Они могут вызывать методы, которые имеют прямой доступ к государству. Однако эти методы всегда реализуются на основе одного наследования.

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

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

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

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