java - 使用接口(interface)的默认方法实现方法 - 矛盾吗?

标签 java inheritance interface java-8 default-method

简介

我已经阅读了多篇关于在 SO 上实现接口(interface)和抽象类的文章。我特别找到了一个我想在这里链接的 - Link - Interface with default methods vs abstract class ,它涵盖了同样的问题。作为公认的答案,建议在可能的情况下使用接口(interface)的默认方法。但是这个答案下面的评论说“这个功能对我来说更像是一个黑客”解释了我的问题。

引入了默认方法以使接口(interface)的实现更加灵活 - 当接口(interface)更改时,实现类中不一定需要(重新)编写代码。因此,使用接口(interface)的默认方法只是为了在所有实现类中实现一个方法 - 引用:“对我来说更像是一个黑客”。

我的考试示例:

类(class)概览:

  • 项目 - 所有项目的抽象父类(super class)
  • 水 - 消耗品
  • 石头 - 不可消耗的物品
  • Consumable - 与一些用于消费品的方法的接口(interface)(这些方法必须被所有实现类覆盖)

  • 结合这些:

    水是物品,是 Prop 消耗品;石头也是一个物品,不实现消耗品。

    我的考试

    我想实现一个所有项目都必须实现的方法。因此,我在类 Item 中声明了签名。
    protected abstract boolean isConsumable(); 
    //return true if class implements (or rather "is consumable") Consumable and false in case it does not
    

    Quick edit: I am aware that instanceof might solve this particular example - if possible think of a more complicated example that makes it necessary to implement the method in the first place. (Thanks to Sp00m and Eugene)



    现在我有几个选择:

    1. Implement the method by hand in every single subclass of Item (this is definitely not possible when scaling the application).


    如上所述,在扩展应用程序时,这将是不切实际的或非常低效的。

    1. Implementing the method inside of the interface as a default method so the Consumable classes already implement the method which is required by the superclass Item.


    这是另一篇文章推荐的解决方案 - 我看到了以这种方式实现它的优点:

    Quote - "The good thing about this new feature is that, where before you were forced to use an abstract class for the convenience methods, thus constraining the implementor to single inheritance, now you can have a really clean design with just the interface and a minimum of implementation effort forced on the programmer." Link



    但在我看来,它似乎仍然与我在介绍中提到的默认方法的最初想法相矛盾。此外,当扩展应用程序并为所有 Consumables 引入更多共享相同实现的方法时(如示例方法 isConsumable()),接口(interface)将实现几个默认方法,这与接口(interface)不实现实际方法的想法相矛盾。

    1. Introducing sub-superclasses instead of an interface - for example the class Consumable as an abstract subclass of Item and superclass of Water.


    它提供了为 Item 中的方法编写默认情况的机会(例如: isConsumable() //return false ),然后在子父类(super class)中覆盖它。这里出现的问题:当扩展应用程序并引入更多子父类(super class)(如 Consumable 类)时,实际的 Items 将开始扩展多个子父类(super class)。这可能不是一件坏事,因为也需要对接口(interface)做同样的事情,但它使继承树变得复杂 - 示例:一个项目现在可能扩展一个子父类(super class) ALayer2,它是 ALayer1 的子父类(super class),它扩展了 Item (layer0) .

    1. Introducing another superclass (thus same layer as Item) - for example the class Consumable as an abstract class which will be another superclass of Water. That means that Water would have to extend Item & Consumable


    此选项提供了灵活性。可以为新的父类(super class)创建一个全新的继承树,同时仍然能够看到 Item 的实际继承。但我发现的缺点是在实际类中实现了这个结构并在以后使用它们 - 示例:我怎么能说:当 Consumable 能够拥有不适合 Items 的子类时,Consumable 是一个 Item .整个转换过程可能会让人头疼——比选项 3 的结构更有可能。

    问题

    实现这种结构的正确选择是什么?
  • 它是我列出的选项之一吗?
  • 它是那些的变种吗?
  • 还是我还没有考虑过的另一种选择?

  • 我选择了一个非常简单的例子——请在回答时牢记 future 实现的可扩展性。提前感谢您的任何帮助。

    编辑#1

    Java 不允许多重继承。这将影响选项 4。
    使用多个接口(interface)(因为您可以实现多个接口(interface))可能是一种解决方法,不幸的是,默认方法将再次成为必要,这正是我最初试图避免的那种实现。 Link - Multiple inheritance problem with possible solution

    最佳答案

    我缺少选项 5(或者我可能没有正确阅读):
    提供 Item 中的方法本身。

    假设消耗品可通过 Consumable 识别-interface 这里是我不能推荐你列出的大部分要点的原因:
    第一个(即在每个子类中实现它)对于像 this instanceof Consumable 这样简单的事情来说太过分了。 .第二个可能还可以,但不会是我的第一选择。第三个和第四个我完全不能推荐。如果我只能给出一个建议,那么它可能是两次考虑继承并且永远不要使用中间类,因为它们在某个时间点让您的生活更轻松。将来当您的类层次结构变得更加复杂时,这可能会伤害您(注意:我并不是说您根本不应该使用中间类;-))。

    那么我会为这个特定案例做什么?我宁愿在抽象 Item 中实现类似以下内容类(class):

    public final boolean isConsumable() {
      return this instanceof Consumable;
    }
    

    但也许我什至不会提供这样的方法,因为它和写作 item instanceof Consumable 一样好首先。

    我什么时候会改用接口(interface)的默认方法?也许当接口(interface)有一个混合字符时,或者当实现对接口(interface)比抽象类更有意义时,例如Consumable的具体功能我可能会在那里提供默认方法,而不是在任何伪实现类中,以便其他类可以再次从它扩展......我也非常喜欢 following answer (or rather the quote) regarding mixin .

    关于您的编辑:“Java 不允许多重继承”……好吧,使用 mixins 可以实现与多重继承类似的东西。您可以实现许多接口(interface),接口(interface)本身也可以扩展许多其他接口(interface)。使用默认方法,您可以重用一些东西:-)

    那么,为什么default接口(interface)中的方法可以使用(或不与接口(interface)定义本身矛盾):
  • 提供已经满足大多数用例的简单或幼稚的实现(其中实现类可以提供特定的、专门的和/或优化的功能)
  • 当从所有参数和上下文中清楚该方法必须做什么时(并且没有合适的抽象类)
  • 对于模板方法,即当它们发出对抽象方法的调用以执行范围更广的某些工作时。典型的例子是 Iterable.forEach ,它使用抽象方法 iterator()Iterable并将提供的操作应用于其每个元素。

  • 感谢 Federico Peralta Schaffner对于建议。

    向后兼容性也是为了完整性,但单独列出功能接口(interface):
    在添加新函数时,默认实现还有助于不破坏现有代码(通过抛出异常以便代码仍然继续编译或通过提供适用于所有实现类的适当实现)。
    对于函数式接口(interface),这是一种相当特殊的接口(interface)情况,默认方法相当重要。功能接口(interface)可以很容易地通过功能来增强,而功能本身不需要任何特定的实现。只需考虑 Predicate 例如..你提供test ,但你也会得到 negate , orand此外(作为默认方法提供)。许多函数式接口(interface)通过默认方法提供额外的上下文函数。

    关于java - 使用接口(interface)的默认方法实现方法 - 矛盾吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52149051/

    相关文章:

    c++ - 使用继承时的复制构造函数

    Python多重继承不显示类变量或第二个继承基类的方法

    Android - 如何创建类似市场或 Shopsavvy 的用户界面

    java - JoinPoint 或 ProceedingJoinPoint 的对象为什么可以调用方法?

    java - 如何使用 Java Regex 查找具有给定字母的单词

    java - 最简单的云存储服务API

    java - java中如何定义子类(基本继承)

    Java R 接口(interface) (JRI) 设置

    java - 使用 JqAssistant 分析@SuppressWarnings

    java - Spring静态资源映射: controller vs addResourceHandler