Default methods also open Java to the type of language construct known as a mixin (ruby) or trait (scala). This blog post illustrates using default methods to add comparison behavior in a class.
Problem to solve...
Java has a cumbersome API used to compare objects. The compareTo method returns an integer whose value and sign defines if the objects are less than, equal to or greater than each other. I want to improve this by defining a interface that has default implementations of isLessThan, isGreaterThan and isEqualTo mixin methods. I want to be able to simply take a class that already implements Comparable and change the interface it implements to one with these new default methods.
To demonstrate this concept I'm going to use a Zombie domain class. The type of a zombie defines how it compares to other zombies. The hierarchy is as follows: Walkers are less than Runners, which are less than Fatties, which are less than Abominations (nod to Zombicide).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Zombie implements Comparable<Zombie> { | |
public enum Type { | |
WALKER, RUNNER, | |
FATTY, ABOMINATION; | |
} | |
Type type; | |
public Zombie(Type type) { | |
this.type = type; | |
} | |
public Type getType() { | |
return type; | |
} | |
@Override | |
public int compareTo(Zombie other) { | |
return type.compareTo(other.getType()); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Zombie1Test { | |
Zombie walker = new Zombie(WALKER); | |
Zombie fatty = new Zombie(FATTY); | |
@Test | |
public void shouldUseCompareMethods() { | |
assertTrue(walker.compareTo(fatty) < 0); | |
assertTrue(fatty.compareTo(walker) > 0); | |
assertTrue(fatty.compareTo(fatty) == 0); | |
} | |
} |
Solution...
This test illustrates the issue I want to fix. To determine if one zombie is bigger than another I need to check the returned value. This makes the code difficult to read especially for developers not familiar with the compareTo function. It is not a fluent API. I can improve this using an interface with some default methods.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public interface Compares<T> extends Comparable<T> { | |
default boolean isLessThan(T other) { | |
return compareTo(other) < 0; | |
} | |
default boolean isGreaterThan(T other) { | |
return compareTo(other) > 0; | |
} | |
default boolean isEqual(T other) { | |
return compareTo(other) == 0; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Zombie implements Compares<Zombie> | |
... remainder of Zombie code is unchanged | |
public class Zombie2Test { | |
Zombie walker = new Zombie(WALKER); | |
Zombie fatty = new Zombie(FATTY); | |
@Test | |
public void shouldUseCompareMethods() { | |
assertTrue(walker.isLessThan(fatty)); | |
assertTrue(fatty.isGreaterThan(walker)); | |
assertTrue(fatty.isEqual(fatty)); | |
} | |
} |
Wrap-up
Default methods add another technique you can use to mange cross cutting behaviors in your classes. At first glance they seem to be a hack used by the API designers to extend the old Java APIs. I think they are solid addition to the language.
No comments:
Post a Comment