Why You Should Use Default Methods in Java 8 Interfaces

Posted by & filed under , .

duke-open With Java 8, Oracle introduced the concept of “default methods” in interfaces (and if you really haven’t heard of this — wtf?? — you can read more here about it: https://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html). This argue some is a step towards multiple inheritance and as such should be banished from the oh, so pure! Java language. Others point out that in fact Groovy has offered this a while back via “traits” — and was about time Java should offer something similar.

Personally I take a more pragmatic view: I don’t care if this breaks any OOP “regulations” nor do I care about whether these should have offered earlier in the language! I care about the fact that they are nowadays available and I strongly recommend everyone should use them! And I’ll highlight here a few reasons why I think you should.

How many projects have you been involved in where there’s a class called something like XyzUtils? (Personally, I have seen tons of those.) And each one of those classes consists normally of a bunch of static methods (and a private constructor on the class, of course, after all the class is not there to be instantiated, just to offer “utility methods”!) — which then get used throughout the whole project.

And that you would argue is fine, right? After all that’s why those utility methods are there in the first place: to group together common constructs of code, which can then be invoked rather than duplicate that code. And I agree with a part of that statement: if you find yourself writing the same code over and over again then you should abstract that in a class/method/something (I say “something” because we have closures too nowadays, we have anonymous inner classes and all sorts!). But I think gone are the days when that “something” should be a static method in a utility class.

Because what happens when you start unit testing methods which rely on these static methods? Well, because there is no (easy) way to mock those classes and methods you end up preparing your test data more — and you spend more time on your unit tests. Time which really you should be spending on your business code — the client pays for the business code not for the unit test code, remember? Read Joel Spolsky’s blog post here where he talks about Jamie Zawinski’s philosophy — I tend to agree with a lot of that.

Let’s consider a simple example — which I think supports my statement about making unit testing easy: we need throughout our entire codebase to format all the dates using a specific format (for the purpose of this let’s settle for the standard European format: dd/MM/yyyy — e.g. 13/03/2016 for Mar/13 2016). There are numerous ways you can achieve that, but a “standard” way to achieve this in the code is to have an utility class, with a static method which does the formatting:

public class DateUtils {
  private static final SimpleDateFormat FMT = new SimpleDateFormat("dd/MM/yyyy");
 
  private DateUtils() {
    // this is such no one instantiates this class
  }
 
  public static String formatDate(Date d) {
    return FMT.format(d);
  }
}

and then in your code you do something like this:

public class MyBusinessLogicClass extends SomeOtherClass {
...
 public void myMethod(Date d) {
  ...
  String formatted = DateUtils.format(d);
  ...
 }
}

I’m sure you would have seen this construct if you’ve been dealing with Java code. And if you’ve been dealing with code like this you also know the usage of that static method in myMethod makes it harder to test! Why? Well, because now when testing myMethod you cannot mock the output of DateUtils.format() and just supply a test string, instead you have to introduce more code into your unit test to build the data you pass into myMethod, such that when the output of it is what you would want to feed into the rest of the code in the method you are trying to test. So if you want to test the rest of the code based on a value for formatted of “13/02/2016” you have to build first a Date for that date representation and pass it into the code, knowing that the static method will return “13/02/2016” and you can test the rest of the code with that value. However, notice that you are creating a different value (the Date instance) to the value you want to actually test with (the String instance “13/02/2016”)!

So your unit test now will look something like this (I’m using Spock framework here btw):

class MyBusinessLogicClassTest extends Specification {
 def "myMethod processes the date correctly"() {
  def c = Calendar.getInstance()
  c.set( Calendar.YEAR, 2016 );
  c.set(Calendar.MONTH, 1);// 0-based!
  c.set(Calendar.DAY_OF_MONTH, 13);
  def d = c.time
  def inst = new MyBusinessLogicClass()
 
  when:
    inst.myMethod(d)
 
  then:
    // test something happens
  }
}

You can see that rather than focusing on writing the test for your method you actually now spend time building the data which will create your test data!

As I said, before the default interface methods in Java 8 that was pretty much the only way to achieve this — notice that MyBusinessClass already extends another class so there was no way one could inherit the method to do the formatting from a base class. However, now we can get rid of our utility class and extract that method into a default :

public interface DateHelper {
  SimpleDateFormat FMT = new SimpleDateFormat("dd/MM/yyyy");
 
  default String formatDate(Date d) {
    return FMT.format(d);
  }
}

(You can tell I love the fact that in an interface everything is public by default and all constants are static final so I don’t have to bother with the extra boilerplate code!)

Then since this is an interface we can now inherit it:

public class MyBusinessLogicClass extends SomeOtherClass implements DateHelper {
...
 public void myMethod(Date d) {
  ...
  String formatted = format(d);
  ...
 }
}

Voila! No need for the static method anymore! And because it’s no longer static we CAN finally mock it — woohoo:

class MyBusinessLogicClassTest extends Specification {
 def "myMethod processes the date correctly"() {
  def d = new Date() //I don't care at all about the value of this
  def inst = Spy(MyBusinessLogicClass)
 
  when:
    inst.myMethod(d)
 
  then:
    1 * inst.format(d) >> "13/02/2016"
    // test something happens
  }
}

As you can see, in this case, we concentrate entirely on our test input (no need to create any intermediary data). Also, if you glance at the length of the code it’s much smaller — and much easier to read! And obviously it’s easier to test.

Not sold yet on it? 🙂