【问题标题】:Interface with default methods vs abstract class, and what's the motivation?默认方法与抽象类的接口,动机是什么?
【发布时间】:2025-12-05 10:40:02
【问题描述】:

上下文

我最近遇到了这个 C# 提案default interface methods 我已经阅读了规范和更重要的是动机。可能我错过了什么,但动机有点让我讨厌。

接口和完全抽象的类之间的唯一实际区别,未来的类可以实现(所以是 [IS A])多个接口,但可以继承(所以是 [IS A])仅来自一个抽象类,(以及所有后果)

我不清楚抽象类和具有默认方法的接口之间的确切区别是什么现在除了,我们可以带来使用默认方法将多个(实现)继承到图片中,这对于抽象类是不可能的。 (我不想打开问题/讨论是好是坏,这不是这里的主题)

但是动机完全不同,三点:

  • "...API 作者在不破坏源代码的情况下在未来版本中向接口添加方法..."。好吧,“API”作者也可以在未来的版本中向抽象类添加方法如果他实现了这些方法而不会破坏任何东西。
  • “...使 C# 能够与面向 Android (Java) 和 iOS (Swift) 的 API 互操作,...”。我认为语言设计决策,尤其是关于抽象和像多重继承这样的 OOP 模式的决策比与 Swift 互操作要高一些。我也觉得,这只是0.0x%的互操作问题,也可以通过其他方式解决。
  • "...事实证明,添加默认接口实现提供了 "traits" 语言功能的元素..."。这是一个非常肤浅的说法,尤其是它指的是*的“特质”。根据定义,特征允许添加方法没有多重继承(与超级有 [IS A] 关系)。然而,接口肯定是关于 [IS A]...不是在谈论这个事实,特征至少是有争议的良好实践

问题

我的问题是真正的区别(或动机)是什么,或者我缺少什么?

【问题讨论】:

  • 回复:第一个项目符号。暂时不要考虑抽象类。想想接口。你已经发布了你的项目的 v1,很多人已经实现了接口,现在你想给接口添加一个方便的方法。这就是场景,与抽象类无关。 没有找到特别令人信服的动机这一事实并不意味着一定有一些其他动机。
  • 谢谢,好点,其实我认为这值得回答。
  • 扩展方法是另一种可以为接口添加“便利方法”的方法。因为不允许接口维护状态,所以添加扩展方法可以让您实现相同的目标。我个人认为他们因这种变化而搅浑水

标签: java c#


【解决方案1】:

引入Default Method之后,好像接口和抽象类是一样的。但是,它们在 Java 8 中仍然是一个不同的概念。

抽象类可以定义构造函数。它们更加结构化,并且可以具有与之关联的状态。相比之下,默认方法只能在调用其他接口方法的情况下实现,而不参考特定实现的状态。因此,两者都用于不同的目的,两者之间的选择实际上取决于场景上下文。

【讨论】:

    【解决方案2】:

    他们在 Java 8 中添加了此功能。因此,您可以添加 Java 标记并询问 Java 开发人员他们可以用它做什么。它显然也存在于HaskellScala 上。

    多继承

    我首先想到的是多重继承。由于一个类可以实现多个接口,例如可以解决diamond problem

    在 Java 中是这样的:

    public interface InterfaceA {
        public default void foo() {
            System.out.println("A -> foo()");
        }
    }
    
    public interface InterfaceB {
        public default void foo() {
            System.out.println("B -> foo()");
        }
    }
    
    private class Test implements InterfaceA, InterfaceB {
        // Compilation error : "class Test inherits unrelated defaults for foo() from types InterfaceA and InterfaceB"
    }
    

    因此,您必须要么实现方法(覆盖默认实现),要么调用super 之一:

    public class Test implements InterfaceA, InterfaceB {
         public void foo() {
            InterfaceB.super.foo();
        }
    }
    

    【讨论】:

    • 虽然服务于类似目的,但 Haskell 类型类不是 Java/C# 接口。它更像是 Scala 的隐式类实现了一个共同的特征。当您调用类型类方法时,实例会使用参数类型静态分派。有人建议 C# 添加如下内容:github.com/dotnet/csharplang/issues/110
    最近更新 更多