Peter Verhas于 2014 年 4 月撰写的文章的翻译。 来自译者:术语“默认方法”刚刚出现在 Java 中,我不确定是否有一个已确定的俄语翻译。我将使用术语“默认方法”,尽管我认为它并不理想。我邀请您讨论更成功的翻译。
默认方法是什么
现在,随着 Java 8 的发布,您可以向接口添加新方法,以便接口与实现它的类保持兼容。如果您正在开发一个被从基辅到纽约的许多程序员使用的库,这一点非常重要。在 Java 8 之前,如果您在库中定义了接口,则无法向其中添加方法,否则会面临运行接口的某些应用程序在更新时崩溃的风险。那么,在 Java 8 中你可以不再害怕这个了吗?你不能。 向接口添加默认方法可能会使某些类无法使用。 让我们首先看看默认方法的好处。在Java 8中,该方法可以直接在接口中实现。(接口中的静态方法现在也可以实现,但那是另一回事了。)在接口中实现的方法称为默认方法,并由default关键字表示。如果一个类实现了一个接口,它可以(但不是必须)实现该接口中实现的方法。该类继承了默认实现。这就是为什么在更改类实现的接口时无需修改类。多重继承?
如果一个类实现了多个(比如两个)接口,并且它们实现了相同的默认方法,事情就会变得更加复杂。该类将继承哪个方法?答案是否定的。在这种情况下,类必须实现该方法本身(直接或通过从另一个类继承它)。如果只有一个接口有默认方法,而在另一个接口中相同的方法是抽象的,则情况类似。Java 8 试图遵守纪律并避免出现模棱两可的情况。如果在多个接口中声明方法,则该类不会继承默认实现 - 您将收到编译错误。尽管如此,如果您的类已经编译,您可能不会收到编译错误。Java 8 在这方面还不够健壮。这是有原因的,我不想讨论(例如:Java 版本已经发布,讨论的时间已经过去了,总的来说,这不是他们的地方)。- 假设您有两个接口,并且一个类实现了这两个接口。
- 其中一个接口实现了默认方法 m()。
- 您编译所有接口和类。
- 您可以通过将没有 m() 方法的接口声明为抽象方法来更改它。
- 您仅编译修改后的接口。
- 开始上课吧。
- 使用抽象 m() 方法更改接口并添加默认实现。
- 编译修改后的界面。
- 运行类:错误。
示例代码
为了演示上述内容,我为 C.java 类创建了一个测试目录,并为文件 I1.java 和 I2.java 中的接口创建了 3 个子目录。测试的根目录包含 C.java 类的源代码。基目录包含适合执行和编译的接口版本:接口 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
兼容目录包含 I2 接口的一个版本,该版本将 m() 方法声明为抽象方法,并且出于技术原因,还包含 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,如果我们将接口编译到兼容目录中,我们将有两个仍然可用于运行该类的接口:
~/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$
GO TO FULL VERSION