我完全不知道为什么不起作用:
interface Test {
default void doMagic() {
System.out.println("Abracadabra");
}
}
class TestImpl implements Test {
}
class SpecialTestImpl extends TestImpl {
public void doMagic() {
Test.super.doMagic(); // Error: No enclosing instance of the type Test is accessible in scope
}
}
这是一些奇怪的Eclipse错误消息(它也无法应对Lamdas,所以也许Mars还没有完全准备好Java 8)?
我可以通过让SpecialTestImpl
直接实现Test
来修复它(这会产生警告,因为它是不必要的)或覆盖TestImpl
中的方法(由于相同的原因会产生警告) )。
那为什么我不能调用超级方法呢?
我的猜测是因为如果我能够直接致电Test.super.doMagic()
,那么在TestImpl
中实施该方法会破坏SpecialTestImpl
的API,即使它不应该。但如果我让SpecialTestImpl
实现Test
并以这种方式调用默认方法,那也是如此。
答案 0 :(得分:11)
这不是Eclipse的错误,它是预期的行为。只需使用super.doMagic();
,它就可以了。您无法调用Test.super.doMagic()
,因为doMagic()
可以在TestImpl
超类中重新实现。在这种情况下,TestImpl
实现必须完全隐藏Test
实现,使其无法访问。
答案 1 :(得分:4)
虽然很清楚原始代码是否被正确拒绝,但尚未解决,但正如您已经写过的那样,让SpecialTestImpl
直接实现Test
突然允许您调用default
方法。
interface Test {
default void doMagic() {
System.out.println("Abracadabra");
}
}
class TestImpl implements Test {
}
class SpecialTestImpl extends TestImpl implements Test {
public void doMagic() {
Test.super.doMagic(); // look, I can invoke that method
}
}
有趣的是,当您将doMagic()
的具体实现插入TestImpl
类时,编译器会停止接受此变通方法(至少使用javac
)。所以这个......特征......似乎有点无意义,你可以使用这种调用,只要它与super.doMagic()
相比没有效果。这可能是故意的吗?
我查看了规范,发现了以下内容:
§15.12.1. Compile-Time Step 1: Determine Class or Interface to Search
...
如果表单为
TypeName . super . [TypeArguments] Identifier
,则:
- 如果TypeName既不是类也不是接口,则是编译时错误。
...
否则,TypeName表示要搜索的接口,I。
设T是直接包含方法调用的类型声明。如果我不是T的直接超接口,或者如果存在T,J的其他直接超类或直接超接口,那么这是一个编译时错误,这样J就是I的子类型。
因此,此处T
为SpecialTestImpl
,I
为Test
,T
具有直接超级超类TestImpl
,TestImpl
1}}是Test
的子类型。
因此,无论TestImpl
是否具有实际doMagic()
实现,都不应该使用此解决方法,但会引发编译时错误。所以这是一个似乎涵盖所有现有编译器实现的错误。
答案 2 :(得分:4)
这是设计上的;你可以叫你的直接超级方法,但不是你的祖父母。
通过类似于类来考虑这个:
class A {
void m() { }
}
class B extends A { ... }
class C extends B {
void m() {
super.m(); // OK
}
}
想象一下,如果C在A中明确调用实现,绕过B的实现是可以的。这将完全被破坏! B无法强制执行其代表不变量。这就是最重要的意思 - 如果B覆盖A中的方法,那么被覆盖的方法只能从 从B中访问。
默认超级调用的限制尝试跟踪此目标(尽管多重继承使得这更加棘手。)您可以调用您的直接超级;你不能通过试图直接打电话给你的祖父母来结束你的超级。这同样会破坏覆盖的含义。
答案 3 :(得分:3)
那为什么我不能调用超级方法呢?
因为它确实不是super
类。 super仅适用于直接子类,它指的是它的父级。
正如你所说,你只需让SpecialTestImpl
实现Test并调用默认方法或调用超类TestImpl
中的已实现方法。