我认为我对Java中的接口引入默认方法感到有些困惑.据我了解,其想法是可以在不破坏现有代码的情况下将默认方法引入现有接口.
如果我使用非抽象类实现接口,我(当然)必须定义接口中所有抽象方法的实现.如果接口定义了默认方法,我将继承该方法的实现.
如果我实现两个接口,我显然必须实现两个接口中定义的抽象方法的并集.我继承了所有默认方法的实现; 但是,如果两个接口中的默认方法之间发生冲突,我必须在我的实现类中重写该方法.
这听起来不错,但是下面的场景呢?
假设有一个接口:
package com.example ;
/**
* Version 1.0
*/
public interface A {
public void foo() ;
/**
* The answer to life, the universe, and everything.
*/
public default int getAnswer() { return 42 ;}
}
Run Code Online (Sandbox Code Playgroud)
和第二个界面
package com.acme ;
/**
* Version 1.0
*/
public interface B {
public void bar() ;
}
Run Code Online (Sandbox Code Playgroud)
所以我可以写下面的内容:
package com.mycompany ;
public class C implements com.example.A, com.acme.B {
@Override
public void foo() {
System.out.println("foo");
}
@Override
public void bar() {
System.out.println("bar");
}
public static void main(String[] args) {
System.out.println(new C().getAnswer());
}
}
Run Code Online (Sandbox Code Playgroud)
所以这应该没问题,的确如此
java com.mycompany.C
Run Code Online (Sandbox Code Playgroud)
显示结果42.
但现在假设acme.com对B进行了以下更改:
package com.acme ;
/**
* Version 1.1
*/
public interface B {
public void bar() ;
/**
* The answer to life, the universe, and everything
* @since 1.1
*/
public default int getAnswer() {
return 6*9;
}
}
Run Code Online (Sandbox Code Playgroud)
据我了解,介绍这种方法应该是安全的.但是如果我现在针对新版本运行现有的com.mycompany.C,我会收到运行时错误:
Exception in thread "main" java.lang.IncompatibleClassChangeError: Conflicting default methods: com/example/A.getAnswer com/acme/B.getAnswer
at com.mycompany.C.getAnswer(C.java)
at com.mycompany.C.main(C.java:12)
Run Code Online (Sandbox Code Playgroud)
这并不奇怪,但这并不意味着将默认方法引入现有接口总是存在破坏现有代码的风险吗?我错过了什么?
Roh*_*ain 14
虽然在两个接口中添加具有相同名称的默认方法会使代码无法编译,但是一旦解决了编译错误,在编译接口和实现接口的类之后获得的二进制文件将向后兼容.
因此,兼容性实际上是关于二进制兼容性.这在JLS§13.5.6 - 接口方法声明中进行了解释:
添加默认方法或将方法从抽象更改为默认方法不会破坏与预先存在的二进制文件的兼容性,但可能会导致
IncompatibleClassChangeError
预先存在的二进制文件尝试调用该方法.如果限定类型T是两个接口I和J的子类型,则会发生此错误,其中I和J都声明具有相同签名和结果的默认方法,并且I和J都不是另一个的子接口.换句话说,添加默认方法是二进制兼容的更改,因为它不会在链接时引入错误,即使它在编译时或调用时引入了错误.在实践中,通过引入默认方法发生的意外冲突的风险类似于向非最终类添加新方法的风险.如果发生冲突,向类添加方法不太可能触发LinkageError,但是在子级中意外覆盖该方法可能会导致不可预测的方法行为.这两个更改都可能在编译时导致错误.
你得到的原因IncompatibleClassChangeError
可能是因为,C
在B
界面中添加默认方法之后,你没有重新编译你的类.
另见:
归档时间: |
|
查看次数: |
1655 次 |
最近记录: |