Java 8的默认方法:可以做什么和不能做什么?

什么是默认方法

在Java 8发行版中,您可以修改接口以添加新方法,以便该接口与实现该接口的类保持兼容。 如果您要开发一个库,该库将由基辅到纽约的几位程序员使用,那么这非常重要。 在Java 8出现之前,如果您在库中发布了接口,则您不能添加新方法,而不必冒险在接口中实现的某些应用程序会随接口的新版本而中断。

使用Java 8,这种恐惧消失了吗? 没有。

向接口添加默认方法可能会使某些类无法使用。

首先让我们看一下默认方法的要点。

在Java 8中,可以在接口中实现一种方法。 (从Java8开始,静态方法也可以在接口中实现,但这是另一回事。)在接口中实现的方法称为默认方法,用关键字default表示为修饰符。 当类实现接口时,它可以但不必实现已经在接口中实现的方法。 该类继承默认实现。 这就是为什么当接口实现更改时可能不需要触摸类的原因。

多重继承?

当一个具体的类实现多个(例如两个)接口并且这些接口实现相同的默认方法时,事情就变得复杂起来。 该类将继承哪个默认方法? 答案是否定的。 在这种情况下,该类必须自己实现该方法(直接实现或通过继承更高级别的类)。

当只有一个接口实现默认方法而另一个仅将其声明为抽象方法时,也是如此。 Java 8试图受到约束,并避免“隐式”的事情。 如果在多个接口中声明了这些方法,则不会继承任何默认实现,则将出现编译时错误。

但是,如果您已经编译了类,则不会出现编译时错误。 这样Java 8不一致。 它有其原因,我不想在这里详述或出于各种原因而进入辩论(例如:发布已结束,辩论时间很长,并且从未在此平台上使用)。

  • 假设您有两个接口,还有一个实现这两个接口的类。
  • 接口之一实现默认方法m()
  • 您编译所有接口和类。
  • 您更改不包含方法m()的接口,以将其声明为抽象方法。
  • 仅编译修改后的接口。
  • 运行课程。

多重继承
在这种情况下,该类将运行。 您不能使用修改后的接口再次对其进行编译,但是如果它是使用较旧版本进行编译的:它仍然可以运行。 现在

  • 修改具有抽象方法m()的接口并创建默认实现。
  • 编译修改后的接口。
  • 运行类:失败。

当有两个接口为同一方法提供默认实现时,该方法不能在实现类中调用,除非由该类实现(再次:直接或从另一个类继承)。
多重继承2 该类是兼容的。 可以使用新界面加载它。 只要两个接口中都没有默认实现的方法的调用,它甚至可以开始执行。

样例代码

多重继承目录

为了演示上述内容,我为类C.java创建了一个测试目录,并为文件I1.javaI2.java的接口创建了三个子目录。 测试的根目录在文件C.java包含类C的源代码。 目录base包含适合执行和编译的接口版本。 I1包含具有默认实现的方法m() 。 接口I2目前不包含任何方法。

该类包含一个main方法,因此我们可以在测试中执行它。 它测试是否存在任何命令行参数,因此我们可以轻松地执行该方法,而无需调用方法m()

~/github/test$ cat C.java 
public class C implements I1, I2 {
  public static void main(String[] args) {
    C c = new C();
    if( args.length == 0 ){
      c.m();
    }
  }
}
~/github/test$ cat base/I1.java 
public interface I1 {
  default void m(){
    System.out.println("hello interface 1");
  }	
}
~/github/test$ cat base/I2.java 
public interface I2 {
}

我们可以使用命令行来编译和运行该类:

~/github/test$ javac -cp .:base C.java
~/github/test$ java -cp .:base C
hello interface 1

compatible目录包含声明方法m()抽象的接口I2版本,并且出于技术原因,它包含未I1.java

~/github/test$ cat compatible/I2.java 

public interface I2 {
  void m();
}

这不能用于编译类C

~/github/test$ javac -cp .:compatible C.java 
C.java:1: error: C is not abstract and does not override abstract method m() in I2
public class C implements I1, I2 {
       ^
1 error

该错误信息非常准确。 即使我们具有先前编译中的C.class ,并且即使在compatible目录中编译接口,我们仍将有两个接口可用于运行该类:

~/github/test$ javac compatible/I*.java
~/github/test$ java -cp .:compatible C
hello interface 1

wrong的第三个目录包含I2版本,该版本还定义了方法m()

~/github/test$ cat wrong/I2.java 
public interface I2 {
  default void m(){
    System.out.println("hello interface 2");
  }
}

我们甚至不应该去编译它。 即使方法是双重定义的,只要不调用该方法,该类仍然可以执行,但是只要我们尝试调用方法m() ,该类就会失败。 这就是我们使用命令行参数的目的:

~/github/test$ javac wrong/*.java
~/github/test$ java -cp .:wrong C
Exception in thread "main" java.lang.IncompatibleClassChangeError: Conflicting default methods: I1.m I2.m
	at C.m(C.java)
	at C.main(C.java:5)
~/github/test$ java -cp .:wrong C x
~/github/test$

结论

当您开始将库移至Java 8并修改接口以添加默认实现时,您可能不会遇到问题。 至少这是Java 8库开发人员希望将功能方法添加到集合中的方式。 使用您的库的应用程序仍然依赖没有默认方法的Java 7库。 使用和修改不同的库时,冲突的可能性很小。 如何避免这种情况?

像以前一样设计您的库API。 不要轻易依赖默认方法的可能性。 他们是不得已的选择。 明智地选择名称,以避免与其他接口冲突。 我们将学习如何使用此功能来开发Java编程。

翻译自: https://www.javacodegeeks.com/2014/04/java-8-default-methods-what-can-and-can-not-do.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值