Programming

my faceChris Foley is a computer programming enthusiast. He loves exploring new programming languages and crafting nice solutions. He lives in Glasgow, Scotland and works as a software developer.

Mixins in Java

One of the great things with learning new languages is it helps me to think about problems in different ways. After learning a little bit of SML I'm happier using recursive solutions in Java. When I choose wisely, it makes my code a little cleaner. As part of working through the Seven Languages in Seven Days book, I learned about mixins in Ruby and I think they are something I can add to my Java programming toolkit.

I'll start by covering what mixins are with a short Ruby example. Then I'll show a Java 8 implementation. Then I'll discuss some of the shortcomings and the dirty tricks available.

What are Mixins.

All Java programmers know about the Comparable interface. You define a compareTo() method and the result tells you if this object is less than (result < 0), equal to (result == 0) or greater than (result > 0) the argument.

Ruby has the same concept. There are no interfaces but you can implement the spaceship operator <=> which does the same thing as Java's compareTo() method (but with a much cooler name).

Ruby adds an enormous amount of value with the Comparable mixin. If you implement the spaceship operator you get <, <=, >, >=, == and a method called between?() for free. I think it's time for an example:

# This could be any class that knows how to speak.
class Cat
	def speak()
		"meow"
	end
end

# Here is a mixin that gives anything that can
# speak the ability to compose a speech.
module Speech
	def speech()
		result = []
		100.times {result << speak}
		result.join(" ")
	end
end

# We can reopen our class. The include line makes
# it as if all the methods in the module were
# typed in as part of the class.
class Cat
	include Speech
end

# An example of the class using the mixin.
tabby_the_cat = Cat.new
puts tabby_the_cat.speech

A Java Mixin

As cute as Tabby is, I'm not going to implement that in Java. Instead, I'm going to do something similar to Ruby's Comparable mixin. First of all, here is a data class we can add the mixin to. It's just a wrapper for int but it could be anything. This was just the quickest to code up.

package mixin;

/**
 * Just enough of an int wrapper to test RubyComparable.
 */
public class Int implements RubyComparable<Int> {
	
	private int value;

	public Int(int i) {
		this.value = i;
	}

	@Override
	public int compareTo(Int other) {
		return value - other.value;
	}

}

RubyComparable in this example will start out life like this:

package mixin;
public interface RubyComparable<T> extends Comparable<T> {
}

It's just enough to force compareTo() on the implementing class. Java doesn't have operator overloading so we will have to settle for methods names along the lines of isGreaterThan(), etc. Implementing compareTo() is where most Java applications stop, and for good reason. It would be a bore to have to implement a bunch of comparison-operator like methods on every implementing class.

Here is the RubyComparable interface modified to give Int (and any other implementing class) the comparison methods for free:

package mixin;

public interface RubyComparable<T> extends Comparable<T> {

	default boolean isEqualTo(T other) {
		return 0 == compareTo(other);
	}
	
	default boolean isLessThan(T other) {
		return compareTo(other) < 0;
	}
	
	default boolean isGreaterThanOrEqualTo(T other) {
		return !isLessThan(other);
	}
	
	default boolean isLessThanOrEqualTo(T other) {
		return isLessThan(other) || isEqualTo(other);
	}
		
	default boolean isGreaterThan(T other) {
		return !isLessThanOrEqualTo(other);
	}

	default boolean isBetween(T min, T max) {
		return isGreaterThanOrEqualTo(min) && isLessThanOrEqualTo(max);
	}

}

Well, that looks simple to me. I'm using using default methods in Java 8 interfaces. The implementing classes can override them but more likely they will just get them for free. You can implement as many interfaces as you want so adding more mixins is no problem.

Here is the test code I used to develop this. I'm including it because it shows how to use the Int class.

package mixin;

import static org.junit.Assert.*;

import org.junit.*;

public class RubyComparableTest {
	
	private static final Int FIVE = new Int(5);
	private static final Int SIX = new Int(6);
	private static final Int SEVEN = new Int(7);

	@Test
	public void compareTo() {
		assertEquals(0, FIVE.compareTo(FIVE));
		assertEquals(0, SIX.compareTo(SIX));
		assertTrue(FIVE.compareTo(SIX) < 0);
		assertTrue(SIX.compareTo(FIVE) > 0);
	}
	
	@Test
	public void isEqualTo() {
		assertTrue(FIVE.isEqualTo(FIVE));
		assertFalse(FIVE.isEqualTo(SIX));
	}
	
	@Test
	public void isLessThan() {
		assertFalse(SIX.isLessThan(FIVE));
		assertFalse(SIX.isLessThan(SIX));
		assertTrue(FIVE.isLessThan(SIX));
	}

	@Test
	public void isGreaterThanOrEqualTo() {
		assertFalse(FIVE.isGreaterThanOrEqualTo(SIX));
		assertTrue(SIX.isGreaterThanOrEqualTo(FIVE));
		assertTrue(SIX.isGreaterThanOrEqualTo(SIX));
	}
	
	@Test
	public void isGreaterThan() {
		assertFalse(FIVE.isGreaterThan(FIVE));
		assertFalse(FIVE.isGreaterThan(SIX));
		assertTrue(SIX.isGreaterThan(FIVE));
	}
	
	@Test
	public void isLessThanOrEqualTo() {
		assertFalse(SIX.isLessThanOrEqualTo(FIVE));
		assertTrue(FIVE.isLessThanOrEqualTo(SIX));
		assertTrue(FIVE.isLessThanOrEqualTo(FIVE));
	}
	
	@Test
	public void between() {
		assertFalse(FIVE.isBetween(SIX, SEVEN));
		assertFalse(SEVEN.isBetween(FIVE, SIX));
		assertTrue(SIX.isBetween(FIVE, SEVEN));
		assertFalse(SIX.isBetween(SEVEN, FIVE));
	}

}

Alternatives for Java 7 and Earlier

Default methods are new in Java 8. Many of us are stuck with some projects in earlier versions so this technique is not available. What alternatives are there?

Problems with This Approach

Java interfaces cannot store state. Ruby mixins can. I had a quick google before I wrote this blog and people seem to get quite worked up about this. There are all sorts of alternative implementations using AspectJ or a singleton hash map to associate objects with a data class (using weak references to avoid memory leaks).

As above, I think these solutions add too much complexity and a simpler design not using mixins should be sought instead. One solution that I do quite like is to force the implementing class to store the data by including a getter and setter in the mixin interface. Even then, there will be times when there would be too many getters and setters or too much of the object's internals would be exposed in the public interface.

Another problem with Java is the inability to open and modify classes. In my Ruby example, I created the Cat class first and then reopened it later on to include the mixin. This is rather nice because it gives a little flexibility in code organisation. It would have been really handy in my Java example to add the default methods to the Comparable interface or to make Integer implement my mixin. If I could have done either, my demo would have worked without the silly Int class. Of course, this isn't a problem if you own the code you are working with but if you are using closed libraries then it closes some doors.

In Summary

Default methods were never intended to be used as mixins but I find this to be an elegant and convenient use for them. They're not as powerful as Ruby mixins but they add some interesting options when writing Java code. Some of the alternatives may close this gap but the cost in complexity is too high in my opinion.

24 September 2014

Comments