Paul Boddington
Paul Boddington

Reputation: 37655

Default methods and interfaces extending other interfaces

Suppose there are two interfaces Interface1 and Interface2 where Interface2 extends Interface1.

interface Interface1 {

    default void method() {
        System.out.println("1");
    }

    // Other methods
}

interface Interface2 extends Interface1 {

    @Override
    default void method() {
        System.out.println("2");
    }

    // Other methods
}

Suppose I want to create a class that implements Interface2 but I want method() to be the version in Interface1. If I write

class MyClass implements Interface1, Interface2 {

    public void method() {
        Interface1.super.method();
    }
}

I get the compilation error:

bad type qualifier in default super call: redundant interface Interface1 is extended by Interface2

It is possible to get around this by creating a third interface:

interface Interface3 extends Interface1 {

    default void method() {
        Interface1.super.method();
    }
}

Then:

class MyClass implements Interface1, Interface2, Interface3 {

    public void method() {
        Interface3.super.method();
    }
}

This compiles fine, and if I instantiate a new MyClass and invoke method(), the output is 1 as expected.

So my question is, given that it is so easy to get around the restriction that you can only write InterfaceName.super.method() for the most specific interface in a chain, what is the reason for the restriction? What problems are prevented by disallowing you from writing Interface1.super.method() in the first place?

Upvotes: 32

Views: 4129

Answers (1)

Radiodef
Radiodef

Reputation: 37875

This is exactly addressed by the JLS in 15.12.3. "Compile-Time Step 3: Is the Chosen Method Appropriate?".

If the form is TypeName . super . [TypeArguments] Identifier, then:

  • […]
  • If TypeName denotes an interface, let T be the type declaration immediately enclosing the method invocation. A compile-time error occurs if there exists a method, distinct from the compile-time declaration, that overrides (§9.4.1) the compile-time declaration from a direct superclass or direct superinterface of T.

The JLS goes on to explain why the rule is in place:

In the case that a superinterface overrides a method declared in a grandparent interface, this rule prevents the child interface from "skipping" the override by simply adding the grandparent to its list of direct superinterfaces. The appropriate way to access functionality of a grandparent is through the direct superinterface, and only if that interface chooses to expose the desired behavior.

So it more or less exists specifically to stop you from doing what you're trying to do.

But the JLS also seems to acknowledge your workaround:

(Alternately, the developer is free to define his own additional superinterface that exposes the desired behavior with a super method invocation.)

Upvotes: 18

Related Questions