Java 8 default method readability
Asked Answered
C

4

8

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.

Criminality answered 25/1, 2015 at 14:24 Comment(3)
Why was this downvoted? Please add a comment.Criminality
The naming 'IDefaultMethod' is ugly old Hungarian Notation, made canon at Microsoft and then rightly thrown out -- except (for some incomprehensible reason) for Interfaces in C#. Not pretty, and should be kept out in Java code.Simeon
@PerLindberg I am very well aware of the fact that preceeding an I for an interface name should be avoided. In this particular example, the only puprose of the interface is to demonstrate default methods and thus the explicit I because the interface is... well.. an interface and nothing else in this example.Criminality
G
7

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.

Gaudery answered 25/1, 2015 at 15:25 Comment(5)
While it maybe the most logical keyword to be used, it does not make it the most readable choice.Criminality
@bot Well, that's an opinion. I can't argue with that. Just out of curiosity, how do you think it should have been?Gaudery
I think the super keyword could have been dropped. Interface.method instead of Interface.super.method is more readable. It comes at a cost of not being able to differentiate between a static and non-static call. But again, there are many such cases in Java already. Consider calling a static member method of a class from an instance method of the same class as an example. You don't know if the method being called is static or not until you see the signature.Criminality
Well, I could agree on using something different than the keyword super for all cases (classes, interfaces, etc), but I would never agree on doing something different in this case than what we have been doing in the rest of the language. Also I am not sure what you meant by "there are many cases in Java already". I cannot think of one. I know you can invoke a static method from a instance, but the opposite is not possible for obvious reasons. What did you have in mind when you mentioned it?Gaudery
When I invoke a static method from an instance method, you don't know that I am invoking a static method unless you look at the signature of the method being invoked. We already have this kind of ambiguity present in the Java language so it wouldn't harm to get rid of the super keyword in case of default methods if it's only purpose is to distinguish between a static and non-static call..Criminality
U
5

This is simply an extension to default methods of the usual approach to accessing members of superclasses (JLS 15.11):

The form T.super.Identifier refers to the field named Identifier of the lexically enclosing instance corresponding to T, but with that instance viewed as an instance of the superclass of T.

Essentially, there can be ambiguity about which member is being referred to when a class has more than one ancestor (whether because of default methods or just because it has multiple superclasses in a hierarchy). The super keyword is the analog of Outer.this in an inner class; it means that you want to get "this, but the stuff I would see inside the body of that superclass instead of the member inside this subclass".

Upsydaisy answered 25/1, 2015 at 14:34 Comment(7)
I am sorry but I am still not able to wrap my head around the IDefaultMethod.super call. While Outer.this means get me the Outer this corresponding to the inner this, what does IDefaultMethod.super mean? Get me the super of the IDefaultMethod interface? The general usage of super refers to the parent. When I say super.something, I mean the parents something. It's not intuitive to read IDefaultMethod.super and think the same.Criminality
@bot It means "the version of this method attached to IDefaultMethod", as opposed to "the version of this method attached to AbstractDefaultMethodImpl". The super is necessary to distinguish it from a static method.Upsydaisy
You can't have a static and a non-static method with the same name within the same interface or class. Why would you need the super keyword to distinguish it from a static method when you can't have a static mayOrMayNotImplementThisMethod() method in IDefaultMethod?Criminality
I think the misunderstanding comes from your definition of super. Still loosely said T.super refers to the current object, but with the current object viewed as an instance of the superclass T of the current class. In this context T can be an interface. Though while it can't ever be instantiated, it does have method bodies for the default methods that could be targeted by T.super. As such the use of super as keyword is correct (and sensible).Rubadub
While it serves the purpose to distinguish whether we are calling a static or non static method, it's still not readable from a non-programming point of view. IDefaultMethod.super would mean IDefaultMethod's parent.Criminality
Nope. If it helps, T.super is a reverse polish notation of what could also be written as super(Class<T> clazz). It returns a an instance of the current object as if it were an instance of T. You understand T to always be the parent class but this view is too strict. Also note that: You don't get an object of type T. You get the current object as if it were an instance of T. It is a small but important difference.Rubadub
@mpkorstanje : Your comment does make me look at it differently. Thanks :)Criminality
P
1

super refers to a class or interface you inherit from. It is means you want to call a method ignoring the fact it has been overridden.

If you used this you would be referring to this class (or a sub-class) and thus have infinite recursion.

Piefer answered 25/1, 2015 at 14:52 Comment(4)
Agreed. But IDefaultMethod.super just doesn't seem right. To me, IDefaultMethod.mayOrMayNotImplementThisMethod would be way more readable.Criminality
@bot But that form would be ambiguous with a static method.Upsydaisy
@chrylis Default methods are only supported in Interfaces. You can't have a static and a non-static method with the same name within the same interface or class. Where would the ambiguity arise then?Criminality
Exactly because it would give the impression a static method is being called. A static method can not call anything outside of that static scope while a default method can call other interface methods. This provides an easy handhold to understanding what could be affected.Rubadub
H
1

Java 8 interfaces also have static methods.

If you say, IDefaultMethod.mayOrMayNotImplementThisMethod();
Then it is a way to call static method, which also seems correct as it is similar to how we access static members of class.

For default method, if 'super' is not used then they might have used 'this', which does not make sense as 'this' belongs to class where we are making method call.

My opinion is, you are correct as it does not provide good readability but seems it is as per language design.

Heighten answered 19/2, 2017 at 7:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.