Extract method refactor with Java 8 default method

Java 8 default methods can be a handy tool for refactoring interface contracts. You can use this trick in case you want to add a new interface method that can be expressed in terms of an existing one. You don’t want to change the implementors of the interface (at least for now).

Let’s take an example on a interface (Loader) with a mis-defined contract. Instead of getting levels where a Loader can be applied to, I wanted the loader to answer this question directly.

The initial code was as follows:

public interface Loader {
  void load(Page page);

  List<Level> getLevelsToBeAppliedOn();
}

boolean shouldBeApplied(Loader loader, Level level) {
    return loader.getLevelsToBeAppliedOn().stream().anyMatch(level::equals);
}

After the refactor, the Loader interface gets a new default method, shouldBeAppliedOn that delegates to the existing method, getLevelsToBeAppliedOn:

public interface Loader {
  void load(Page page);

  List<Level> getLevelsToBeAppliedOn();

  default boolean shouldBeAppliedOn(Level other) {
    return getLevelsToBeAppliedOn().stream().anyMatch(other::equals);
  }
}

The client code is simplified:

boolean shouldBeApplied(Loader loader, Level level) {
  return loader.shouldBeAppliedOn(level);
}

Now this method doesn’t make our intent any clearer and can be safely inlined.

This refactoring didn’t break the implementors of the Loader interface. The next step would be to implement the shouldBeAppliedOn method in all Loader derivates. When done, I could convert the default method to an abstract method (i.e. remove its body) and remove the original method (getLevelsToBeAppliedOn).

Advertisements