14

Let's say there are following types:

public interface Base {

    default void sayHi(){
        System.out.println("hi from base");
    }
}

public interface Foo extends Base {
    @Override
    default void sayHi(){
        System.out.println("hi from foo");
    }
}

public interface Bar extends Base {
}

public class MyClass implements Foo, Bar {
    public static void main(String[] args) {
        MyClass c = new MyClass();
        c.sayHi();
    }
}

In this scenario, if main is executed, "hi from foo" is printed. Why does Foo's implementation take precedence? Doesn't Bar inherit sayHi() from Base, since if MyClass was to only implement Bar, the Base implementation would be called? So it would make sense for the code to still not compile. Also, since Bar should have Base's implementation of sayHi(), why can't I override it in MyClass like:

@Override
public void sayHi() {
    Bar.super.sayHi();
}

The following error occurs when trying to do so:

bad type qualifier Bar in default super call method, sayHi() is overridden in Foo

5
  • @Casey What IDE are you using? Eclipse gives another error. Commented May 12, 2016 at 21:13
  • @JornVernee That was from IntelliJ. What does Eclipse output? Commented May 12, 2016 at 21:14
  • @Casey Illegal reference to super method sayHi() from type Base, cannot bypass the more specific override from type Foo Commented May 12, 2016 at 21:15
  • Eclipse's message is definitely clearer. Just tried compiling from the command line and get bad type qualifier Bar in default super call Bar.super.sayHi(); method sayHi() is overridden in Foo on Java 1.8.0_45 Commented May 12, 2016 at 21:18
  • @Casey I have 1.8.0_91 Maybe the difference is there. Commented May 12, 2016 at 21:20

3 Answers 3

16

This behavior is specified using almost your exact example in JLS 9.4.1, just with some names changed around:

interface Top {
    default String name() { return "unnamed"; }
}
interface Left extends Top {
    default String name() { return getClass().getName(); }
}
interface Right extends Top {}

interface Bottom extends Left, Right {}

Right inherits name() from Top, but Bottom inherits name() from Left, not Right. This is because name() from Left overrides the declaration of name() in Top.

The JLS doesn't seem to give any especially concrete reason that I can see; this is just how the Java designers decided inheritance would work.

Sign up to request clarification or add additional context in comments.

5 Comments

Makes sense. Kind of seems like a design flaw. Overriding a default method forever hides the original implementation, which in a complex hierarchy could be critical. Seems like letting the concrete class decide which implementation to use would be a better idea.
@aiguy that's...kind of the point of overriding. That's always been true for normal class inheritance, that overriding hides the supertype implementation completely.
well, in normal class inheritance you could never have 2 parent implementations. My point is if say in a complex hierarchy someone adds an override of original default implementation, it could break the entire hierarchy if some of the concrete classes rely on original implementation.
@aiguy if the concrete classes rely on the original implementation, they're doing it wrong.
@aiguy Well, this one answers an actual question you posed. The other answers an implicit question that you didn't actually pose, so I guess this one should win. But, Casey needs the rep more than Louis (sorry, Louis), so accept the other one anyway. ;-)
13

This is by design. From JLS 15.12.3:

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.

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. (Alternately, the developer is free to define his own additional superinterface that exposes the desired behavior with a super method invocation.)

Comments

1

Why does Foo's implementation take precedence? and since Bar should have Base's implementation of sayHi(), why can't I override?

  • Please check this default methods rule from Oracle...

Methods that are already overridden by other candidates are ignored. This circumstance can arise when supertypes share a common ancestor.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.