Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8 default method readability

Java 8 introduces the concept of default methods. Consider the following interface with a default method :

public interface IDefaultMethod {

    public abstract void musImplementThisMethod();
    public default void mayOrMayNotImplementThisMethod() {
        System.out.println(" This method is optional for classes that implement this interface ");
    }

}

And a class that implements this interface :

    public class DefaultMethodImpl implements IDefaultMethod {

        @Override
        public void musImplementThisMethod() {
                System.out.println("This method must be implementd ");
        }


        @Override
        public void mayOrMayNotImplementThisMethod() {
            // TODO Auto-generated method stub
            IDefaultMethod.super.mayOrMayNotImplementThisMethod();
        }

}

I have a question about the readability of the following call in the mayOrMayNotImplementThisMethod :

IDefaultMethod.super.mayOrMayNotImplementThisMethod();

I understand that the reason for explicitly specifying the interface name in the above call is to avoid confusion in case multiple interfaces implemented by the class have the same method. What I don't understand is the meaning of the super keyword in this context. When we say IDefaultMethod.super, what exactly are we referring to here? Wouldn't IDefaultMethod.mayOrMayNotImplementThisMethod() be more readable than IDefaultMethod.super.mayOrMayNotImplementThisMethod()? Removing the super keyword makes it more readable at the cost of distinguishing between a static or non static method call.

like image 879
Chetan Kinger Avatar asked Jan 25 '15 14:01

Chetan Kinger


1 Answers

I will try to contribute to the discussion by following my own reasonings about this.

Using Classes

First, let's see how this work with simple Java classes:

class Barney {
    void foo() { System.out.println("Barney says foo"); }
}

class Fred extends Barney {
    @Override void foo() { super.foo(); }
}

In this case if we invoke the method foo in a Fred instance it will ask for the implementation of the foo method in its super class and execute that one.

Evidently, none of these others would work:

@Override void foo() { foo(); } //means this.foo() so infinite recursion
@Override void foo() { Barney.foo(); } //means a static method

There is a third configuration that we could do:

class Barney {

    void foo() { System.out.println("Barney says foo"); }

    class Fred extends Barney {
        @Override void foo() { Barney.this.foo(); }
    }

}

In this case if we invoke foo in a instance of Fred, since this instance would have a bond with its enclosing instance, this invocation would invoke the foo method in the enclosing instance of Barney.

For instance

new Barney().new Fred().foo();

So, the use of Barney.this here is used to navigate between instances in an inner/outer relation.

Using Interfaces

Now let's try to repeat the same ideas with interfaces.

interface Barney {
    default void foo() { System.out.println("Barney says foo"); }
}


interface Fred extends Barney {
    @Override default void foo() { Barney.super.foo(); }
}

As far as I can tell, this is exactly the same thing as with classes, it is just that in this case since an interface can inherit from more than one interface we simply qualify the super keyword with the name of the interface we are targeting in this case.

The meaning is the same, we want to invoke the "implementation" of the foo method in the super interface explicitly named.

As with classes, the following would not work:

@Override default void foo() { super.foo(); } //can't be sure of which interface
@Override default void foo() { this.foo(); } //infinite recursion
@Override default void foo() { Barney.foo(); } //static method
@Override default void foo() { Barney.this.foo(); } //not an inner class relation

So, the logical choice here is Interface.super.method().

A question here would be whether we cab ever have a use case like Interface.this.method when using interfaces.

Not really, because interfaces represent a static context, therefore there is never a concept like that of inner classes between interfaces. So this is never possible.

interface Barney {
    default void foo() { System.out.println("Barney says foo"); }

    interface Fred extends Barney {
            @Override default void foo() { Barney.this.foo(); }
    }
}

Basically, this is not possible because the code above does not mean that an instance of Fred would need to exist within the context of an instance of Barney. This is just a static inner interface and instances of it can exist independently of any instances of the parent interface.

So, that's why this would not be a good choice.

So, as you can see, after all this the use of super kind of makes sense, or at least I hope I have explained myself well enough to convey that idea.

like image 123
Edwin Dalorzo Avatar answered Oct 20 '22 01:10

Edwin Dalorzo