首页 文章

Java 8中的抽象类和接口有什么区别?

提问于
浏览
64

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

那是什么?

尽我所知,唯一剩下的差异(除了可能是引擎效率之外的东西)是抽象类遵循传统的Java单继承,而接口可以有多重继承(或者如果你愿意,可以有多个实现) . 这引出了另一个问题 -

新的Java 8接口如何避免diamond Problem

5 回答

  • 68

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

    当两个接口为具有相同签名的相同方法提供默认实现时,通过简单地不允许类实现两个接口来避免钻石问题 .

  • 4

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

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

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

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

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

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

    如果我想从 B 继承 method() .
    如果 Dclass ,则同样适用 .

    要更多地了解Java 8中接口和抽象类之间的区别,请考虑以下 Team

    interface Player {
    
    }
    
    interface Team {
        void addPlayer(Player player);
    }
    

    理论上,您可以提供 addPlayer 的默认实现,以便您可以将玩家添加到例如玩家列表中 .
    可是等等...?
    如何存储玩家列表?
    答案是,即使您有可用的默认实现,也无法在界面中执行此操作 .

  • 8

    有一些非常详细的答案,但它们似乎缺少一点,我至少认为是抽象课程的少数理由之一:

    抽象类可以包含 protected 成员(以及具有默认可见性的成员) . 接口中的方法是隐含的 public .

  • 5

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

    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 a C(即A的一种类型),或

    • 是D a B a C(即两种类型的A) .

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

  • 17

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

    仍有一些更为重要的差异 . 参考这篇文章:

    Interface with default methods vs Abstract class in Java 8

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

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

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

    以上示例产生以下输出:

    InterfaceA foo
    

    Case 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
    

相关问题