Java 8 default Methods

0

If a class implements an interface, it needs to either implement all the methods defined by the interface or inherit the implementation from the superclass. If the class hasn’t implemented even one single method, it will have to declare itself as an abstract class.

Now consider the scenario where an interface is published but at a later stage the author wanted to add new methods to the interface.

If the author decides to go ahead and add the methods, it will force the users of the interface to implement the newly added methods. This doesn’t look right. A library designer will certainly avoid this approach and instead will think upon alternative ways to provide the additional functionality.

Though there are ways to re-factor, a much straightforward approach would be to provide a default implementation in the modified interface itself.

Java 8 introduces default methods feature that allows you to provide a default implementation for methods in an interface.  The existing classes implementing the interface will automatically inherit the default implementations and they are not forced to provide one explicitly. If they have already provided one then it will override the default method.

Adding a default method

The default method is pretty much like a regular method except that it starts with default keyword.  For example, the <codeforEach in the Iterable interface.

    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }

A default method cannot directly change the state as the interface doesn’t have any state. It can only use its arguments or call the other methods declared in the interface.

Default method resolution Rule

Consider the scenario where a class implements multiple interfaces and the same default method exists in more than one interface. Which default method will the class resolve to?
What happens if the superclass too has got one implementation? Let’s review each case and understand how the class resolves the method.

Class inheriting the default method

My example revolves around forests. There are different types of forests. Each forest has its own dangerous animal. My interface IForest has a default method mostDangerousAnimal. We all know from stories that Lion is the king of forest so the default method returns ‘Lion’. In this example, we will see the simplest case where a class extends an interface and ends up inheriting the default method.

IForest:

package com.javarticles.java8.stream;

public interface IForest {
    default String mostDangerousAnimal() {
        return "Lion";
    }
}

Now GirForest is an implementation of IForest. It inherits the parent interfaces’s default method.

GirForest:

package com.javarticles.java8.stream;

public class GirForest implements IForest {
}

DefaultMethodExample:

package com.javarticles.java8.stream;


public class DefaultMethodExample {

    public static void main(String[] args) {
        System.out.println("Gir Forest's most dangerous animal: " + new GirForest().mostDangerousAnimal());
    }
}

Output:

Gir Forest's most dangerous animal: Lion
Class inheriting default method

Class inheriting default method

Class inheriting sub-interface’s default method

Consider the scenario where an interface extends a parent interface, provides its own version of default method and the class extends the sub-interface.
I will now introduce you to a much denser forest also called Jungle. This acts as the parent interface.

IJungle:

package com.javarticles.java8.stream;

public interface IJungle {
    default String mostDangerousAnimal() {
        return "Tiger";
    }
}

Child interface IRainForest extends IJungle and provides its own implementation of the default method mostDangerousAnimal. Surprisingly, ‘Mosquito’ is the most dangerous animal as it carries deadly diseases with it.

IRainForest:

package com.javarticles.java8.stream;

public interface IRainForest extends IJungle {
    default String mostDangerousAnimal() {
        return "Mosquito";
    }
}

Now class AmazonForest implements the sub-interface IRainForest.

AmazonForest:

package com.javarticles.java8.stream;

public class AmazonForest implements IRainForest {
}
Class inheriting sub-interface's default method

Class inheriting sub-interface’s default method

DefaultMethodExample:

package com.javarticles.java8.stream;


public class DefaultMethodExample {

    public static void main(String[] args) {
        System.out.println("Gir Forest's most dangerous animal: " + new GirForest().mostDangerousAnimal());
        System.out.println("Amazon Forest's most dangerous animal: " + new AmazonForest().mostDangerousAnimal());
    }
}

As you can see AmazonForest ends up inheriting the sub-interface IRainForest‘s version of mostDangerousAnimal().

Output:

Gir Forest's most dangerous animal: Lion
Amazon Forest's most dangerous animal: Mosquito

Class inheriting super class’s default method

In this scenario, a class inherits the default method from both the super class and the sub-interface.
IFictionalForest is a fictional forest where Dinosaur rule.

IFictionalForest:

package com.javarticles.java8.stream;

public interface IFictionalForest extends IForest {
    default String mostDangerousAnimal() {
        return "Dinosaur";
    }
}

FictionalForest implements IFictionalForest. It is a fictional forest where ‘Giants’ live so it will override the default method.

FictionalForest:

package com.javarticles.java8.stream;

public class FictionalForest implements IFictionalForest {
    public String mostDangerousAnimal() {
        return "Giant";
    }
}

Enchanted forest is not only a fictional forest but is also denser so class EnchantedForest extends class FictionalForest and implements IRainForest.

EnchantedForest:

package com.javarticles.java8.stream;

public class EnchantedForest extends FictionalForest implements IRainForest {
}

In this scenario, both superclass FictionalForest and sub-interface IRainForest has provided their own versions of implementations but the class will resolve the default method to the one provided by its superclass FictionalForest.

DefaultMethodExample:

package com.javarticles.java8.stream;


public class DefaultMethodExample {

    public static void main(String[] args) {
        System.out.println("Gir Forest's most dangerous animal: " + new GirForest().mostDangerousAnimal());
        System.out.println("Amazon Forest's most dangerous animal: " + new AmazonForest().mostDangerousAnimal());
        System.out.println("Fictional Forest's most dangerous animal: " + new FictionalForest().mostDangerousAnimal());
        System.out.println("Enchanted Forest's most dangerous animal: " + new EnchantedForest().mostDangerousAnimal());
    }
}

Output:

Gir Forest's most dangerous animal: Lion
Amazon Forest's most dangerous animal: Mosquito
Fictional Forest's most dangerous animal: Giant
Enchanted Forest's most dangerous animal: Giant
Jurasic Forest's most dangerous animal: Dinosaur
Class method is preferred over default method

Class method is preferred over default method

Multiple inheritance

Our final scenario is about a class implementing multiple interfaces where both the interfaces provide their own version of default method.
Class JurasicForest implements both IFictionalForest and IRainForest.

JurasicForest:

package com.javarticles.java8.stream;

public class JurasicForest implements IFictionalForest, IRainForest {
}

In thi ssituation, there is an ambiguity and compiler throws error.

Error:

Duplicate default methods named mostDangerousAnimal with the parameters () and () are inherited from the types IRainForest and IFictionalForest
You can resolve this by modifying the class to explicitly implement the default method. If it wants to inherit one of the interface’s version, it can do it by calling <interface>.super.<method>.

JurasicForest:

package com.javarticles.java8.stream;

public class JurasicForest implements IFictionalForest, IRainForest {
    public String mostDangerousAnimal() {
        return IFictionalForest.super.mostDangerousAnimal();
    }
}

DefaultMethodExample:

package com.javarticles.java8.stream;


public class DefaultMethodExample {

    public static void main(String[] args) {
        System.out.println("Gir Forest's most dangerous animal: " + new GirForest().mostDangerousAnimal());
        System.out.println("Amazon Forest's most dangerous animal: " + new AmazonForest().mostDangerousAnimal());
        System.out.println("Fictional Forest's most dangerous animal: " + new FictionalForest().mostDangerousAnimal());
        System.out.println("Enchanted Forest's most dangerous animal: " + new EnchantedForest().mostDangerousAnimal());
        System.out.println("Jurasic Forest's most dangerous animal: " + new JurasicForest().mostDangerousAnimal());
    }
}

Output:

Gir Forest's most dangerous animal: Lion
Amazon Forest's most dangerous animal: Mosquito
Fictional Forest's most dangerous animal: Giant
Enchanted Forest's most dangerous animal: Giant
Jurasic Forest's most dangerous animal: Dinosaur
Multiple Inheritence

Multiple Inheritence

Download source code

This was an example about Java 8 Default Method feature. You can download the source code here: defaultMethodExamples.zip

Share.

Comments are closed.