Shadowing default method of an interface
Asked Answered
B

2

7

Consider the following case,

interface IFace1 {
    default void printHello() {
        System.out.println("IFace1");
    }
}

interface IFace2 {
    void printHello();
} 

public class Test implements IFace1, IFace2 {

    public static void main(String[] args) {
        Test test = new Test();
        test.printHello();

        IFace1 iface1 = new Test();
        iface1.printHello();

        IFace2 iface2 = new Test();
        iface2.printHello();
    }

    @Override
    public void printHello() {
        System.out.println("Test");
    }
}

In above example I am getting following output which is quite expected.

Test
Test
Test

I have been reading about Java-8 default methods and specifically about Extending Interfaces That Contain Default Methods

2nd bullet : Redeclare the default method, which makes it abstract.

In above example where I have two interfaces which have default method with same name and when I implemented both I was only able to reach to the implementation of printHello of Test which refers to IFace2.

I have few questions about this,

  1. How can I reach to the printHello method of IFace1 and if I can't than why ?
  2. Doesn't this behavior keep me away from the intended nature of IFace1 which is may be now shadowed by other method ?

Quote says, you can make the default method abstract in it's child interface. For example,

interface IFace2 extends IFace1 {
    void printHello();
}

Here when I implement IFace2 I won't be actually able to reach default method of IFace1 that is exactly what is happening in my case.

Ballarat answered 10/9, 2015 at 17:51 Comment(0)
D
7

It seems that you are a bit confused by the presence of a default method. So let’s forget for a moment that IFace1.printHello() is a default method. So then, there is a clear situation: Test implements two interfaces, IFace1 and IFace2, which happen to have a method with the same name and signature.

Test implements that method and therefore implements the method for both interfaces. The new feature of default methods does not change anything about this logic. Moreover, the language designers took care that adding a default method does not affect the behavior of existing code, so if your class implements the method, the presence of default methods becomes irrelevant.

However, if you write code that is aware of the presence of default methods, you may invoke it, if it is declared or inherited by a direct super interface, i.e. in your code you may use IFace1.super.printHello() to invoke the default method of IFace1.

The rules are not much different to the rules of super classes. If you change the interfaces so that interface IFace2 extends IFace1 and still declares printHello() as an abstract method, then this abstract method does override the default method and you can’t invoke IFace1.super.printHello() from within Test anymore.

As said, these rules are not much different to ordinary instance methods. If Test declares a method printHello(), that’s the only method that you can invoke by a reference to a Test instance, regardless whether its declared type is Test, IFace1 or IFace2. Only implementation methods of Test itself may do super invocations.


The main difference comes into play when the possible multiple inheritance of interfaces is involved. If your class Test does not implement the method printHello(), it depends on the inheritance relationship of the two interfaces, what will happen

  • If IFace2 extends IFace1, it’s abstract method redeclares the default method, hence a compiler error occurs, as Test must implement the abstract method
  • If IFace2 does not extend IFace1, there are ambiguously two inheritable methods with the same name and signature, therefore Test will not inherit the default method, and a compiler error occurs, as Test must implement the abstract method
  • If IFace1 extends IFace2, Test will inherit the default method. It will also inherit it if Test does not implement IFace2, but this should come at a surprise…
Didi answered 10/9, 2015 at 18:15 Comment(0)
W
5

How can I reach to the printHello method of IFace1 and if I can't than why ?

You can only do so within an instance method of a type that implements IFace1 with

IFace1.super.printHello(); // only if IFace1 declares a default implementation of the method

In other words, you can't do it through a simple reference of type IFace1 or Test (or whatever). That would break encapsulation.

Doesn't this behavior keep me away from the intended nature of IFace1 which is may be now shadowed by other method ?

There's no shadowing going on here. You've overriden the method, so the overriden one gets invoked.

Concerning your 3rd question, you haven't extended any interfaces, so I don't see the relevance.

If you really did have

interface IFace2 extends IFace1 {
    void printHello();
}

with

public class Test implements IFace2 {

then you would not be able to access IFace1's default implementation of the method. You can never skip super types to access implementations higher up in the inheritance hierarchy.

Whaleback answered 10/9, 2015 at 17:56 Comment(3)
@TAsk: new Test().printHello() will always invoke the method declared in Test, regardless of how you define the interfaces or whether Test implements them or not.Didi
@TAsk I don't understand your confusion. Why do you expect anything other than the overriden method to be invoked?Whaleback
@TAsk What do you mean by meaningful? If you don't override it in Test, your code won't compile.Whaleback

© 2022 - 2024 — McMap. All rights reserved.