3

I'm studying Java 8, and I have encountered a behavior with default methods I cannot understand completely.

First, an "old-school" Java snippet which compiles and runs perfectly:

abstract class A {
    public void print() {
        System.out.println("A");
    }
}
interface B {
    void print(); // implicitly public and abstract
}
class C extends A implements B {
    public void useInheritedPrint() {
      print(); // prints A
    }
}

C inherits an implemented print() from A and an abstract print() from B which is considered correctly implemented.

But if A becomes an interface with default method print(), as follows:

interface A { 
    default void print() { // implicitly public
        System.out.println("A");
    }
}
interface B {
    void print(); // implicitly public and abstract
}
class C implements A,B {
    public void useInheritedPrint() {
      print(); // should print A
    }
}

Even though C still inherits print() from A, the compiler complains about the fact that C is not abstract (same thing does not happen if A is a class as shown before). If C becomes abstract as follows :

abstract class C implements A, B {
  public void useInheritedPrint() {
    print();
  }
}

then the compiler complains about the fact the C inherits both a default (from A) and an abstract print() (from B).

The solution is either to define an abstract print() in C, as:

class C implements A,B {
    public void useInheritedPrint() {
      print(); // should print A
    }
    public abstract void print(); // shall be implemented by a concrete subclass
}

Or to override print(), as:

class C implements A,B {
    public void useInheritedPrint() {
      print(); // should print A
    }
    public void print() { 
      // do something
    }
}

Does anyone know if there is a particular reason for this asymmetric behavior between inheriting a default method from an interface and inheriting it from a parent class?

Is deadly diamond of death involved here somehow (I can't see why)?

1d0m3n30
  • 430
  • 6
  • 12

1 Answers1

3

You need to implement print() in C:

class C implements A,B {
    public void useInheritedPrint() {
      print(); // should print A
    }

    @Override
    public void print() {
        A.super.print();
    }
}

The rationale is that, if you inherit two possibly conflicting methods with the same signature, you need to explicitly implement the method and choose one of the implementations (or define a new one).

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • I know I need to, I asked why. – 1d0m3n30 Jun 19 '16 at 13:34
  • 3
    Because the language designers chose to design it like that, probably to avoid accidentally inheriting an implementation without realizing it. – JB Nizet Jun 19 '16 at 13:36
  • I do not agree, you can have two possibly conflicting methods with the same signature only if both of them are default (=with a body) and in this situation Java forces the class which implements them to override the method (otherwise it does not compile). But my situation does not seem the case since one of the two, the print() of B, is abstract, therefore no conflict is possibile. – 1d0m3n30 Jun 19 '16 at 14:30
  • 1
    We can only speculate. The rule could have been different, the rule could have been simpler, or more complex, or whatever, but it's what it is, and you can't change it. – JB Nizet Jun 19 '16 at 14:40
  • 4
    @1d0m3n30 You are using an optimistically narrow definition of "conflict". Yes, the two methods have the same signature - but implementing an interface means also implementing its _specification_. Since the class that declares the behavior of `print()` here (A) has no awareness of B's specification for `print()`, it seemed awfully optimistic to just assume that `A.print()` would meet the contract of `B.print()` in C (the first place where the two come together.) Since the cost of being explicit is so low, we erred on the side of requiring more specificity here. – Brian Goetz Jun 20 '16 at 00:23
  • https://stackoverflow.com/a/31618886/7095884 – olivmir Aug 23 '23 at 06:26