默认方法可以被覆盖
您说:
因此,只有实现类中的其他方法可以被覆盖。
这里的“只有”一词是不正确的。接口中的默认方法确实可以被实现类覆盖。因此,“default”关键字在此处使用,意味着:如果运行时没有其他实现代码,则使用此方法代码。
这里有一个荒谬的例子,我们定义了一个接口Fruit
,其中包含一个默认方法isJuicy
,返回true
。我们有两个子类Orange
和Banana
,第一个没有覆盖isJuicy
,因此其行为来自默认方法。第二个演示了您可以覆盖默认方法。在这里,我们看到覆盖返回false
。
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 2013-09年在State of the Lambda中所述:
默认方法(…)的目的是使接口在其初始发布后以兼容的方式进行演进。
我个人的看法是,程序员通常不会期望行为内置于您的接口中。在Java中,接口的经典用途是根据方法签名定义合同,而不是定义行为(代码)。因此,请仅在最后一种情况下将行为(代码)添加为默认方法。
相反,将您的接口定义为合同,不包含默认方法。至少起初没有默认方法;您可能会像Brian Goetz和Java团队一样发现需要稍后添加默认方法。但是,从只有合同开始。
然后定义一个实现该接口的抽象类。任何要在各种子类之间共享的行为(代码)都可以移动到此抽象类中。
然后继续定义子类、具体类,这些类继承自您的抽象类。
使用接口+抽象类+具体类的经典和常见方法,您可以灵活地进行更改,并使测试变得更容易(使用存根而不是真实类),同时有效地共享来自一个位置的代码,但允许需要时进行覆盖。