Java8中抽象类和接口之间的区别是什么?

Java8中抽象类和接口之间的区别是什么?,java,interface,abstract-class,java-8,diamond-problem,Java,Interface,Abstract Class,Java 8,Diamond Problem,在Java中,抽象类和接口之间有一个微妙但重要的区别:。抽象类可以有它们,接口不能。Java8引入了接口的默认实现,这意味着这不再是接口和抽象类之间的关键区别 那是什么呢 正如我所能说的,唯一剩下的区别(也许除了一些幕后效率的东西)是抽象类遵循传统的Java单继承,而接口可以有多继承(或者多个实现,如果您愿意的话)。这就引出了另一个问题- 新的Java 8接口如何避免钻石问题?钻石问题的定义是模糊的。多重继承可能会出现各种各样的问题。幸运的是,它们中的大多数可以在编译时轻松检测到,编程语言支持简

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

那是什么呢

正如我所能说的,唯一剩下的区别(也许除了一些幕后效率的东西)是抽象类遵循传统的Java单继承,而接口可以有多继承(或者多个实现,如果您愿意的话)。这就引出了另一个问题-

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

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

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

  • 是D a B*和*a C(即a的一种类型),或
  • 是D a B*或*a C(即两种类型的a)

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

接口不能具有与其关联的状态

抽象类可以具有与其关联的状态

此外,不需要实现接口中的默认方法。因此,通过这种方式,它不会破坏现有的代码,因为当接口确实收到更新时,实现类不需要实现它。
因此,您可能会得到次优代码,但如果您希望获得更优的代码,那么您的工作就是覆盖默认实现

最后,如果出现菱形问题,编译器将警告您,您需要选择要实现的接口

为了更多地了解钻石问题,请考虑下面的代码:

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 {

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

接口D继承了method()表单类型B和C的不相关默认值

解决办法是:

interface D extends B, C {
    @Override
    default void method() {
        B.super.method();
    }
}
如果我想从
B
继承
方法()
如果
D
,则同样适用

为了更详细地说明java 8中接口和抽象类之间的区别,请考虑下面的<>代码>团队/> >:

interface Player {

}

interface Team {
    void addPlayer(Player player);
}
理论上,您可以提供
addPlayer
的默认实现,这样您就可以将玩家添加到例如玩家列表中。
但是等等…?
如何存储玩家列表?

答案是,即使有可用的默认实现,您也无法在接口中实现这一点。

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


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

有一些非常详细的答案,但他们似乎忽略了一点,至少我认为这是为抽象类提供的理由之一:

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

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

不过,还有一些更关键的区别。请参阅本帖:

新的Java8接口如何避免菱形问题

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

以上示例产生以下输出:

InterfaceA foo
案例2:您正在扩展一个基类,并使用默认方法实现一个接口。编译器为您解决菱形问题,您不必像第一个示例中那样解决它

上述示例产生以下输出:

Diamond base foo

你读过吗?在你的链接中,它写道:“Java 8在接口上引入了默认方法。如果A、B、C是接口,B、C各自可以为A的抽象方法提供不同的实现,从而导致菱形问题。任何一个类D都必须重新实现该方法(其主体可以简单地将调用转发给其中一个超级实现),否则该歧义将作为编译错误被拒绝。[6]“@Reimeus我没有,谢谢@莱昂纳多·布鲁宁斯,另一个回答澄清了我的疑问。谢谢。没有成员变量并不能解决菱形问题——在没有使用任何内部状态的情况下,相同方法的两个冲突实现仍然可能存在。菱形问题的解决方法是不允许一个类实现两个具有冲突默认实现的接口。@Philipp:您也可以有两个没有菱形的冲突方法。OP特别提出了钻石问题。关于钻石,最重要的是国家。@PradeepSimha你能链接到为你澄清这一点的答案吗?我仍能找到解决问题的办法
InterfaceA foo
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