问题
I'm totally lost on why that won't work:
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
}
}
Is this some weird Eclipse error message (it's not able to cope with Lamdas either, so maybe Mars isn't entirely Java 8 ready, yet)?
I can fix it by letting SpecialTestImpl implement Test directly (which yields a warning, because it's unnecessary) or overriding the method in TestImpl (which yields a warning for the same reasons).
So why wouldn't I be able to call the super method?
My guess was because if I was able to call Test.super.doMagic() directly, implementing the method in TestImpl would break the API for SpecialTestImpl even though it shouldn't. But that is also true if I let SpecialTestImpl implement Test and call the default method that way.
回答1:
It's not an Eclipse bug, it's expected behavior. Just use super.doMagic();, it works fine. You cannot call the Test.super.doMagic() as later the doMagic() can be reimplemented in TestImpl superclass. In this case the TestImpl implementation must completely shadow the Test implementation making it inaccessible.
回答2:
While it is clear know that the original code is correctly rejected, it hasn’t been addressed yet, but as you have already written, letting SpecialTestImpl directly implement Test suddenly allows you to invoke the default method.
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
}
}
Interestingly, when you insert a concrete implementation of doMagic() into the TestImpl class, the compiler stops accepting this workaround (at least with javac). So this …feature… seems to be a bit pointless, you can use this kind of invocation as long as it has no effect compared to super.doMagic(). Can this be intentional?
I looked up the specification and found the following:
§15.12.1. Compile-Time Step 1: Determine Class or Interface to Search
…
If the form is
TypeName . super . [TypeArguments] Identifier, then:
- It is a compile-time error if TypeName denotes neither a class nor an interface.
…
Otherwise, TypeName denotes the interface to be searched, I.
Let T be the type declaration immediately enclosing the method invocation. It is a compile-time error if I is not a direct superinterface of T, or if there exists some other direct superclass or direct superinterface of T, J, such that J is a subtype of I.
So, here T is SpecialTestImpl and I is Test and T has a direct super superclass, TestImpl such that TestImpl is a subtype of Test.
So this workaround shouldn’t be possible but provoke a compile-time error, regardless of whether TestImpl has an actual doMagic() implementation or not. So it’s a bug that seems to cover all existing compiler implementations.
回答3:
This is by design; you can call your direct super-methods, but not your grandparents.
Think about this by analogue to classes:
class A {
void m() { }
}
class B extends A { ... }
class C extends B {
void m() {
super.m(); // OK
}
}
Imagine if it was OK for C to explicitly call the implementation in A, bypassing that of B. This would be totally broken! B could not enforce its representational invariants. That's what overriding means -- if B overrides a method from A, that overridden method is only accessible from B.
The restrictions for default super-calls attempt to track this goal (though multiple inheritance makes this far trickier.) You can call your immediate supers; you can't make an end-run around your supers by trying to directly call your grandparents. That would similarly undermine what override means here.
回答4:
So why wouldn't I be able to call the super method?
Because it's really not exactly the super class. super only works in the direct child class which refers to it's parent.
As you said you just let SpecialTestImpl implement Test and call the default method or call the implemented method in your super class TestImpl .
来源:https://stackoverflow.com/questions/32606652/using-the-default-method-of-grandparent-interface