我理解为什么 Java 中不允许类的循环继承,但我不明白为什么不允许接口的循环继承。为了显示:
interface Foo extends Bar {/*methods and constants*/}
interface Bar extends Foo {/*methods and constants*/}
接口不需要实例化,那么是什么阻止它们相互扩展呢?
顺便说一句,我读了这个问题,但这不是关于接口而是类: Java 中的循环继承层次结构
提前致谢。
我理解为什么 Java 中不允许类的循环继承,但我不明白为什么不允许接口的循环继承。为了显示:
interface Foo extends Bar {/*methods and constants*/}
interface Bar extends Foo {/*methods and constants*/}
接口不需要实例化,那么是什么阻止它们相互扩展呢?
顺便说一句,我读了这个问题,但这不是关于接口而是类: Java 中的循环继承层次结构
提前致谢。
No, but extension of an interface is a way of splitting up the agreement. Remember, an interface is an agreement to provide an implementation of a set of methods.
public interface A extends B {
public void myMethod();
public void myOtherMethod();
}
You're saying interface A
is defined by these methods and all the methods in interface B
. Now if interface B
says..
public interface B extends A {}
you're saying that interface B
is defined by the methods in interface A
. Well what defines interface A
. A couple of methods and interface B
. And what defines interface B
? Interface A
, which is defined by a couple of methods and interface B
! See where this is going?
It makes no logical sense to allow this.
可能没有理论上的困难,但这会造成不必要的复杂化。举几个例子:
当前遍历类接口(通过递归调用Class.getInterfaces()
)保证产生有限的结果,可能有重复,但尽管如此。例如,这样的代码是有效的:
private static void fillInterfaces(Class<?> clazz, Set<Class<?>> set) {
if(clazz == null) return;
for (Class<?> iclass : clazz.getInterfaces()) {
set.add(iclass);
fillInterfaces(iclass, set);
}
fillInterfaces(clazz.getSuperclass(), set);
}
public static Set<Class<?>> getAllInterfaces(Class<?> clazz) {
Set<Class<?>> result = new HashSet<>();
fillInterfaces(clazz, result);
return result;
}
类似的代码已经被编写并在许多地方工作。您的提议在此处提供循环接口将导致无限递归。
当前(在 Java-8 中)接口也可以为其父接口定义默认实现,必要时替换父实现。例如:
interface A {
default public String getX() {return "A";}
}
interface B extends A {
default public String getX() {return "B";}
}
static class C implements A, B {} // ok, C.getX() returns "B"
如果 now A extends B
,则A
获胜:
interface A extends B {
default public String getX() {return "A";}
}
interface B {
default public String getX() {return "B";}
}
static class C implements A, B {} // ok, C.getX() returns "A"
但是如果两者都A extends B
和B extends A
怎么办?谁会赢?new C().getX()
会打印什么?还是应该是新型的编译错误?
一般来说,这样的特性似乎带来的问题多于产生的好处。
请参阅 Java 语言规范9.1.3 超接口和子接口:
如果以下任何一项为真,则接口 I依赖于引用类型 T:
我直接依赖于T。
我直接依赖于依赖于 T 的 C 类(第 8.1.5 节)。
I 直接依赖于一个依赖于 T 的接口 J(递归地使用这个定义)。
如果接口依赖于自身,则为编译时错误。
如果在运行时检测到循环声明的接口,则在加载接口时,
ClassCircularityError
会抛出 a(第 12.2.1 节)。
至于为什么,我喜欢安迪·特纳的评论:
如果
Foo extends Bar
,那么 的每个实例Foo
也是一个Bar
。如果Bar extends Foo
,那么 的每个实例Bar
也是一个Foo
。如果两者都被允许为真,那么满足这两个条件的唯一方法就是 ifFoo == Bar
。