首页 > 解决方案 > Java 接口:在实现类中使用默认方法实现

问题描述

我有一个接口,它的 2 个或更多实现说,

public interface IProcessor {
  default void method1() {
   //logic
  }
  default void method2() {
   //logic
  }
  public void method3();
  public void method4();
}

在这里,method1method2实现逻辑在所有多个多个实现类中是通用的。所以定义为默认方法。所以只有其余的方法可以在实现类中被覆盖。

public CarImpl implements IProcessor {
  @Override 
  public void method3() {
    //logic
  }
  @Override 
  public void method4() {
    //logic
  }
}

public VanImpl implements IProcessor {
  @Override 
  public void method3() {
    //logic
  }
  @Override 
  public void method4() {
    //logic
  }
}

有没有更好的方法可以在没有默认方法和相应实现类中没有冗余代码的情况下实现这一点?因为如果default方法中的代码增加,那么界面看起来很笨拙。

标签: javaoopdesign-patterns

解决方案


可以覆盖默认方法

你说:

所以只有其余的方法可以在实现类中被覆盖。

那里的“唯一”一词是不正确的。接口中的默认方法确实可以被实现类覆盖。因此default这里用作关键字的词,意思是:如果在运行时没有其他实现代码,则使用此方法代码。

这是一个愚蠢的设计示例,我们定义了一个带有返回Fruit的默认方法的接口。我们有两个子类,和。第一个没有覆盖,所以它的行为来自默认方法。第二个演示您可以覆盖默认方法。在这里,我们看到了覆盖 return 。isJuicytrueOrangeBananaisJuicyfalse

package work.basil.example;

public class OverridingDefault
{
    public static void main ( String[] args )
    {
        OverridingDefault app = new OverridingDefault();
        app.demo();
    }

    private void demo ( )
    {
        System.out.println( "new Orange().isJuicy(): " + new Orange().isJuicy() );
        System.out.println( "new Banana().isJuicy(): " + new Banana().isJuicy() );
    }

    public interface Fruit
    {
        default boolean isJuicy ( )
        {
            return true;
        }
    }

    public class Orange implements Fruit
    {
    }

    public class Banana implements Fruit
    {
        @Override
        public boolean isJuicy ( )
        {
            return false;
        }
    }
}

跑的时候。

new Orange().isJuicy(): true
new Banana().isJuicy(): false

优先使用抽象类而不是默认方法

您询问:

有没有更好的方法可以在没有默认方法且没有相应实现类中的冗余代码的情况下实现这一目标?

我建议您不要为此使用default接口方法。

将默认方法添加到 Java 接口的想法和技术本身并不是一个特性,而是另一个问题的解决方案:在现有接口上改造功能以支持新的 lambda 特性,但不会破坏数百万 Java 程序员的现有代码否则会在向现有接口添加方法时发生。通过在接口上发明default方法,Java 团队能够向现有接口添加更多方法,同时减轻所有现有实现对实现这些新方法的需求。新功能,无需破坏代码,这是 Java 的标志。

正如Brian Goetz在 Lambda 状态2013-09中所述:

默认方法(...)的目的是使接口在初始发布后能够以兼容的方式进行演进。

我自己的观点是,程序员通常不会期望将行为内置到您的接口中。Java 中接口的经典用法是根据方法签名定义合约,而不是定义行为(代码)。所以考虑添加添加行为(代码)作为默认方法仅作为最后的手段。

相反,将您的接口定义为合同,没有默认方法。至少一开始没有默认方法;稍后您可能会发现像 Brian Goetz 和 Java 团队一样需要稍后添加默认方法。但从合同开始。

然后定义一个实现该接口的抽象类。任何要在各个子类之间共享的行为(代码)都可以移到这个抽象类中。

然后继续定义从抽象类继承的子类,具体类。

使用接口 + 抽象类 + 具体类的经典和通用方法,您可以灵活地进行更改,并使测试更容易(使用存根而不是真实类),同时从一个地方有效地共享代码,但允许在需要的地方进行覆盖。


推荐阅读