Blog

Traits, Cakes and Java 8

Whilst reading up on Scala for a project I’m working on I stumbled upon the section which introduced a concept called Traits and how it is used in DI (Cake Pattern). It occurred to me that something similar could be achieved using default methods on interfaces (less dry here) – a new feature available in Java 8. I guessed that someone else must have thought the same thing as myself so took to the web to find out.

As expected, this was not something new, but a consideration made by Brian Goetz when designing this feature back in the day. There are several posts he has made on the subject (Virtual Field Pattern, Default Methods As Traits, Allow default methods to override Object’s methods) which discuss this. It turns out that other people have similar, less than favourable views about using interface methods for such a technique (Sad Pandas).

Let’s have look at the quick example in the Sad Panda article only Panda-ize it.

class Pandas
{
    interface Emotion {
        default void feeling() {System.out.println("happy");}
    }
     
    interface Sensation extends Emotion {
        default void feeling() {System.out.println("some bamboo");}
    }
 
    static class Panda implements Emotion {
        void callFeeling() {feeling();}
    }
 
    static class FeelyPanda extends Panda implements Sensation {
        void callSuperFeeling() {super.feeling();}
    }
 
    public static void main(String[] args)
    {
        System.out.println("=== Feely Panda ===");
        FeelyPanda feelyPanda = new FeelyPanda();
        feelyPanda.feeling();
        feelyPanda.callFeeling();
        feelyPanda.callSuperFeeling();
    }
}

The problem here being that the output may not be what was expected:


=== Feely Panda ===
some bamboo
some bamboo
happy

FeelyPanda seems to lose his way and instead of feeling some bamboo, just feels happy instead by accidentally invoking the super.feeling. As discussed in the Sad Pandas post, this is easier to spot here, but much more difficult in a domain with many more interfaces / methods in the hierarchy.

Scala get around problems with these by being more explicit with the order in which traits are applied, making it easier to understand overriding order. Java 8’s rules are less clear and open to mistakes similar to those illustrated above.

It would seem that the take home from this that you CAN use the default method feature for this type of work, and you could probably get away with it for small domains, but once things get complicated, it will probably only make things worse. Best practice seems to be to just use default methods for what they were intended for – updating old apis whilst still providing backward compatibility, and use the age old ‘composition over inheritence’.

Apparently, you can’t have your traits and eat them 😉

Matt Todd 22/05/2016