5

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.

Stefan S.
  • 3,950
  • 5
  • 25
  • 77
  • 2
    `doMagic` is not a static method, so you cannot invoke it statically. – Buhake Sindi Sep 16 '15 at 10:58
  • Have a look at this [link](http://stackoverflow.com/questions/9560600/java-no-enclosing-instance-of-type-foo-is-accessible) here is not java 8 but it's the same concept and it's the reason why you are getting this particular error because of your invocation . Have a look at Tagir solution. – Amr Sep 16 '15 at 10:59
  • 3
    @BuhakeSindi That's not static method invocation syntax. – Marko Topolnik Sep 16 '15 at 10:59

4 Answers4

11

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.

Tagir Valeev
  • 97,161
  • 19
  • 222
  • 334
  • 1
    It works for his case, but if `TestIImpl` also implementd the method, he would not be able to reach the defender method. Which is a sane design choice, I think. – Marko Topolnik Sep 16 '15 at 11:01
  • 2
    @MarkoTopolnik, exactly! In the same way you cannot call the method of super-super-class which is overridden in superclass. – Tagir Valeev Sep 16 '15 at 11:02
  • 2
    A case of missing the forest for the trees, because Eclipse gave `TestImpl.super.doMagic()` as the standard implementation for the method (which is indeed a bug, if you ask me). :) – Stefan S. Sep 16 '15 at 11:10
  • 1
    @SteffiS. here I would agree with you. I'm using Eclipse Luna as well and this also bugs me. – Tagir Valeev Sep 16 '15 at 11:14
  • 3
    `TestImpl.super.doMagic()` would be correct if `TestImpl` was an `interface`… – Holger Sep 16 '15 at 11:39
4

While it is clear now 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.

John Vasileff
  • 5,292
  • 2
  • 22
  • 16
Holger
  • 285,553
  • 42
  • 434
  • 765
  • Your first sentence seems to wait for some grammatical clean-up :). What hasn't been addressed yet? What do we know? – Stephan Herrmann Sep 25 '15 at 20:15
  • @Stephan Herrmann: at the time of writing this answer, the were only answers addressing the fact that the first version of the code is correctly rejected, but what hasn’t addressed at that point, is the thing following the phrase, the fact that letting the class directly implement the interface suddenly gets compiled. I admit, that sentence isn’t an easy read due to the insertion stating that the second fact already has been written by the OP. Well, “know” should be “now”, but I’m not very eager on editing posts for single letter fixes. I’ll update once I have a bit more to change… – Holger Sep 28 '15 at 07:47
4

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.

Brian Goetz
  • 90,105
  • 23
  • 150
  • 161
3

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 .

Suresh Atta
  • 120,458
  • 37
  • 198
  • 307