5

I understand that if a class implements multiple interfaces containing default methods of same name, then we need to override that method in the child class so as to explicitly define what my method will do.
Problem is, see the below code :

interface A {
    default void print() {
        System.out.println(" In interface A ");
    }
}

interface B {
    default String print() {
        return "In interface B";
    }
}

public class C implements A, B {

    @Override
    public String print() {
        return "In class C";
    }

    public static void main(String arg[]) {
        // Other funny things
    }
}

Now interface A and B both have a default method with name 'print' but I want to override the print method of interface B - the one that returns a string and leave A's print as is. But this code doesn't compile giving this :

Overrides A.print
The return type is incompatible with A.print()

Clearly compiler is trying to override A's print method, and I have no idea why !

Zippy
  • 3,826
  • 5
  • 43
  • 96
Raman Verma
  • 113
  • 9

1 Answers1

3

This is not possible.

8.4.8.3:

If a method declaration d1 with return type R1 overrides or hides the declaration of another method d2 with return type R2, then d1 must be return-type-substitutable for d2, or a compile-time error occurs.

8.4.5:

A method declaration d1 with return type R1 is return-type-substitutable for another method d2 with return type R2 iff any of the following is true:

  • If R1 is void then R2 is void.

  • If R1 is a primitive type then R2 is identical to R1.

  • If R1 is a reference type then one of the following is true:

    • R1, adapted to the type parameters of d2, is a subtype of R2.

    • R1 can be converted to a subtype of R2 by unchecked conversion.

    • d1 does not have the same signature as d2, and R1 = |R2|.

In other words, void, primitive- and reference-returning methods may only override and be overridden by methods of that same respective category. A void method may only override another void method, a reference-returning method may only override another reference-returning method, and so on.

One possible solution to the problem you're having could be to use composition instead of inheritance:

class C {
    private A a = ...;
    private B b = ...;
    public A getA() { return a; }
    public B getB() { return b; }
}
Radiodef
  • 37,180
  • 14
  • 90
  • 125
  • My main concern was to override the 'print()' method of interface B, your mentioned links do make it very clear that : `It is a compile-time error if a class C inherits a concrete method whose signature is override-equivalent with another method inherited by C.` So I understand 'what' happens but the 'why' is still not clear ! Why can't the compiler identify by the method's signature that I am overriding interface B's print( ) method. Or if you could give me some insight why this is a bad practice ? – Raman Verma Dec 17 '17 at 16:23
  • The language just doesn't allow for two methods to have the same signature but different return types, except when one return type is assignable to the other. You can't declare both a method `void m()` and a method `String m()` in the same class. I don't think there's a technical reason this is impossible for the class file format or JVM. The language designers just decided not to allow it because a method call like `m()` without an assignment is ambiguous with respect to which method is being called. – Radiodef Dec 17 '17 at 16:29
  • Yeah seems logical, thanks for sharing your knowledge. – Raman Verma Dec 17 '17 at 18:18
  • 2
    @Radiodef: It *is* possible in the class file format. So yes, there is no technical reason. – Holger Dec 18 '17 at 10:26
  • 1
    @RamanVerma: since Java doesn’t even allow the class `C` to implement `A` and `B` at the same time, it is irrelevant, whether it could identify which method you’re overriding. The reason is that it can not identify which method you’re *invoking* when saying `someInstanceOfC.print()`, at least not without changing the language rules in a way, the Java designers do not want. – Holger Dec 18 '17 at 10:30
  • @Holger I could not understand why that is not "exposed" too, but only possible inside the JVM – Eugene Dec 18 '17 at 10:45
  • 1
    @Eugene: to me, it looks consistent to all other design decisions of the original Java language, to make it simple and less error-prone. Even in the current version where target typing exist, choosing among different methods based on the expected return type doesn’t feel like a good idea. – Holger Dec 18 '17 at 12:03
  • 1
    Just to add one more aspect: choosing a method based on return type certainly doesn't gell well with covariant return types. Inheriting a method returning `Number` and another one returning `Integer` would create rather obscure problems. What if you override the `Integer`-returning method, would that automatically override also the `Number`-returning method, or will you end up with two methods? I'm very happy that this mess is not supported. – Stephan Herrmann Dec 18 '17 at 19:31