在Java 8中抽象类和接口之间有什么区别?

时间:2022-09-25 12:54:47

In Java there used to be a subtle but important difference between abstract classes and interfaces: default implementations. Abstract classes could have them, interfaces could not. Java 8 though introduces default implementations for interfaces, meaning this is no longer the critical difference between an interface and an abstract class.

在Java中,抽象类和接口之间曾经有一个微妙但重要的区别:默认实现。抽象类可以有它们,接口不能。Java 8虽然引入了接口的默认实现,这意味着接口和抽象类之间不再是关键的区别。

So what is?

那么是什么呢?

As best as I can tell, the only remaining difference (besides perhaps some under the hood efficiency stuff) is that abstract classes follow traditional Java single-inheritance, whereas interfaces can have multiple-inheritance (or multiple-implementation if you will). This leads me to another question -

正如我所能指出的那样,惟一的区别(除了可能的一些底层效率之外)是抽象类遵循传统的Java单继承,而接口可以具有多重继承(或者如果您愿意的话,可以有多个实现)。这就引出了另一个问题

How do the new Java 8 interfaces avoid the diamond Problem?

新的Java 8接口如何避免钻石问题?

5 个解决方案

#1


65  

Interfaces cannot have state associated with them.

接口不能有与它们相关联的状态。

Abstract classes can have state associated with them.

抽象类可以有与它们相关联的状态。

Furthermore, default methods in interfaces need not be implemented. So in this way, it will not break already existing code, as while the interface does receive an update, the implementing class does not need to implement it.
As a result you may get suboptimal code, but if you want to have more optimal code, then your job is to override the default implementation.

此外,接口中的默认方法不需要实现。这样,它就不会破坏已经存在的代码,因为当接口接收到更新时,实现类不需要实现它。因此,您可能会得到次优代码,但是如果您想拥有更多的最优代码,那么您的工作就是覆盖默认实现。

And lastly, in case a diamond problem occurs, then the compiler will warn you, and you will need to choose which interface you want to implement.

最后,如果出现钻石问题,编译器会警告您,您需要选择要实现的接口。

To show more about the diamond problem, consider the following code:

要了解有关钻石问题的更多信息,请考虑以下代码:

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 {

}

Here I get the compiler error on interface D extends B, C, that:

这里我得到了D接口上的编译错误扩展了B C,即:

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

接口D继承方法()类型B和C的不相关默认值

The fix is:

解决办法是:

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

In case I wanted to inherit the method() from B.
The same holds for if D were a class.

如果我想从b继承方法(),如果D是一个类,也是如此。

To show even more about the difference between interfaces and abstract classes in Java 8, consider the following Team:

要进一步展示Java 8中接口和抽象类之间的差异,请考虑以下团队:

interface Player {

}

interface Team {
    void addPlayer(Player player);
}

You can in theory provide a default implementation of addPlayer such that you can add players to for example a list of players.
But wait...?
How do I store the list of players?
The answer is that you cannot do that in an interface, even if you have default implementations available.

理论上,您可以提供一个默认的addPlayer实现,例如,您可以向列表添加参与者。但是等等…?如何存储玩家列表?答案是,在接口中不能这样做,即使有可用的默认实现。

#2


17  

There have been some very detailed answers, but they seem to be missing one point that I at least consider as one of the very few justifications to have abstract classes at all:

有一些非常详细的答案,但它们似乎漏掉了一点,我至少认为这是仅有的几个有抽象类的理由之一:

Abstract classes can have protected members (and members with default visibility). Methods in interfaces are implicitly public.

抽象类可以有受保护的成员(以及具有默认可见性的成员)。接口中的方法是隐式公共的。

#3


8  

The definition of the diamond problem is a vague. There are all kinds of issues that can occur with multiple inheritance. Fortunately, most of them can be easily detected at compile-time, and the programming languages support simple solutions to work around these issues. Most of these problems are not even specific to the diamond problem. For example, conflicting definitions of methods can also occur without diamonds:

钻石问题的定义是模糊的。在多重继承中会出现各种各样的问题。幸运的是,它们中的大多数可以在编译时很容易检测到,编程语言支持简单的解决方案来解决这些问题。这些问题中的大多数甚至都不是钻石问题所特有的。例如,如果没有钻石,方法的定义也可能出现冲突:

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

The specific problem with diamonds is the question of inclusive vs. exclusive. If you have a type hierarchy where B and C derive from A, and D derives from B and C, then the question is:

钻石的具体问题是包容与排外的问题。如果您有一个类型层次结构,其中B和C来自a, D来自B和C,那么问题是:

  • is D a B *and* a C (i.e. one type of A), or
  • D a B *和*a C(即a的一种),还是
  • is D a B *or* a C (i.e. two types of A).
  • 是D a B *或* a C(即a的两种类型)。

Well, in Java 8 the type A has to be an interface. So it makes no difference, because interfaces have no state. It doesn't matter, that interfaces can define default methods, since they also have no state. They can invoke methods that have direct access to state. However, these methods are always implemented based on single inheritance.

在Java 8中A类型必须是一个接口。这没有区别,因为接口没有状态。没关系,接口可以定义默认方法,因为它们也没有状态。它们可以调用直接访问状态的方法。但是,这些方法总是基于单个继承实现的。

#4


5  

Now that interfaces can contain executable code, lots of use-cases for abstract classes are taken over by interfaces. But abstract classes can still have member variables, while interfaces can't.

既然接口可以包含可执行代码,那么许多抽象类的用例就会被接口接管。但是抽象类仍然可以有成员变量,而接口不能。

The diamond problem is avoided by simply not allowing a class to implement two interfaces when both interfaces provide a default implementation for the same method with the same signature.

当两个接口为具有相同签名的相同方法提供默认实现时,只要不允许类实现两个接口,就可以避免菱形问题。

#5


2  

Java 8 though introduces default implementations for interfaces, meaning this is no longer the critical difference between an interface and an abstract class.

Java 8虽然引入了接口的默认实现,这意味着接口和抽象类之间不再是关键的区别。

Still there are few more critical differences. Refer to this post:

不过,几乎没有什么比这更关键的区别了。请参考这篇文章:

Interface with default methods vs Abstract class in Java 8

在Java 8中使用默认方法和抽象类的接口。

How do the new Java 8 interfaces avoid the diamond Problem?

新的Java 8接口如何避免钻石问题?

Case 1: You are implementing two interfaces, which have same default method, you have to resolve the conflict in your implementation class

案例1:您正在实现两个具有相同默认方法的接口,您必须解决实现类中的冲突

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();
    }
} 

Above example produce below outout:

上面的例子产出如下:

InterfaceA foo

Case 2: You are extending a base class and implementing an interface with default method. Compiler resolves the diamond problem for you and you don't have to resolve it as in first example.

案例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();
    }
}

Above example produce below output:

以上示例输出如下:

Diamond base foo

#1


65  

Interfaces cannot have state associated with them.

接口不能有与它们相关联的状态。

Abstract classes can have state associated with them.

抽象类可以有与它们相关联的状态。

Furthermore, default methods in interfaces need not be implemented. So in this way, it will not break already existing code, as while the interface does receive an update, the implementing class does not need to implement it.
As a result you may get suboptimal code, but if you want to have more optimal code, then your job is to override the default implementation.

此外,接口中的默认方法不需要实现。这样,它就不会破坏已经存在的代码,因为当接口接收到更新时,实现类不需要实现它。因此,您可能会得到次优代码,但是如果您想拥有更多的最优代码,那么您的工作就是覆盖默认实现。

And lastly, in case a diamond problem occurs, then the compiler will warn you, and you will need to choose which interface you want to implement.

最后,如果出现钻石问题,编译器会警告您,您需要选择要实现的接口。

To show more about the diamond problem, consider the following code:

要了解有关钻石问题的更多信息,请考虑以下代码:

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 {

}

Here I get the compiler error on interface D extends B, C, that:

这里我得到了D接口上的编译错误扩展了B C,即:

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

接口D继承方法()类型B和C的不相关默认值

The fix is:

解决办法是:

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

In case I wanted to inherit the method() from B.
The same holds for if D were a class.

如果我想从b继承方法(),如果D是一个类,也是如此。

To show even more about the difference between interfaces and abstract classes in Java 8, consider the following Team:

要进一步展示Java 8中接口和抽象类之间的差异,请考虑以下团队:

interface Player {

}

interface Team {
    void addPlayer(Player player);
}

You can in theory provide a default implementation of addPlayer such that you can add players to for example a list of players.
But wait...?
How do I store the list of players?
The answer is that you cannot do that in an interface, even if you have default implementations available.

理论上,您可以提供一个默认的addPlayer实现,例如,您可以向列表添加参与者。但是等等…?如何存储玩家列表?答案是,在接口中不能这样做,即使有可用的默认实现。

#2


17  

There have been some very detailed answers, but they seem to be missing one point that I at least consider as one of the very few justifications to have abstract classes at all:

有一些非常详细的答案,但它们似乎漏掉了一点,我至少认为这是仅有的几个有抽象类的理由之一:

Abstract classes can have protected members (and members with default visibility). Methods in interfaces are implicitly public.

抽象类可以有受保护的成员(以及具有默认可见性的成员)。接口中的方法是隐式公共的。

#3


8  

The definition of the diamond problem is a vague. There are all kinds of issues that can occur with multiple inheritance. Fortunately, most of them can be easily detected at compile-time, and the programming languages support simple solutions to work around these issues. Most of these problems are not even specific to the diamond problem. For example, conflicting definitions of methods can also occur without diamonds:

钻石问题的定义是模糊的。在多重继承中会出现各种各样的问题。幸运的是,它们中的大多数可以在编译时很容易检测到,编程语言支持简单的解决方案来解决这些问题。这些问题中的大多数甚至都不是钻石问题所特有的。例如,如果没有钻石,方法的定义也可能出现冲突:

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

The specific problem with diamonds is the question of inclusive vs. exclusive. If you have a type hierarchy where B and C derive from A, and D derives from B and C, then the question is:

钻石的具体问题是包容与排外的问题。如果您有一个类型层次结构,其中B和C来自a, D来自B和C,那么问题是:

  • is D a B *and* a C (i.e. one type of A), or
  • D a B *和*a C(即a的一种),还是
  • is D a B *or* a C (i.e. two types of A).
  • 是D a B *或* a C(即a的两种类型)。

Well, in Java 8 the type A has to be an interface. So it makes no difference, because interfaces have no state. It doesn't matter, that interfaces can define default methods, since they also have no state. They can invoke methods that have direct access to state. However, these methods are always implemented based on single inheritance.

在Java 8中A类型必须是一个接口。这没有区别,因为接口没有状态。没关系,接口可以定义默认方法,因为它们也没有状态。它们可以调用直接访问状态的方法。但是,这些方法总是基于单个继承实现的。

#4


5  

Now that interfaces can contain executable code, lots of use-cases for abstract classes are taken over by interfaces. But abstract classes can still have member variables, while interfaces can't.

既然接口可以包含可执行代码,那么许多抽象类的用例就会被接口接管。但是抽象类仍然可以有成员变量,而接口不能。

The diamond problem is avoided by simply not allowing a class to implement two interfaces when both interfaces provide a default implementation for the same method with the same signature.

当两个接口为具有相同签名的相同方法提供默认实现时,只要不允许类实现两个接口,就可以避免菱形问题。

#5


2  

Java 8 though introduces default implementations for interfaces, meaning this is no longer the critical difference between an interface and an abstract class.

Java 8虽然引入了接口的默认实现,这意味着接口和抽象类之间不再是关键的区别。

Still there are few more critical differences. Refer to this post:

不过,几乎没有什么比这更关键的区别了。请参考这篇文章:

Interface with default methods vs Abstract class in Java 8

在Java 8中使用默认方法和抽象类的接口。

How do the new Java 8 interfaces avoid the diamond Problem?

新的Java 8接口如何避免钻石问题?

Case 1: You are implementing two interfaces, which have same default method, you have to resolve the conflict in your implementation class

案例1:您正在实现两个具有相同默认方法的接口,您必须解决实现类中的冲突

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();
    }
} 

Above example produce below outout:

上面的例子产出如下:

InterfaceA foo

Case 2: You are extending a base class and implementing an interface with default method. Compiler resolves the diamond problem for you and you don't have to resolve it as in first example.

案例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();
    }
}

Above example produce below output:

以上示例输出如下:

Diamond base foo