Subscribe to Jack of all Languages        RSS Feed
-----

Mixins in Java

Icon Leave Comment
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 but I'm enclosing it in a spoiler tag because it's long and this post is not about testing.

Spoiler


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?

Common Superclass:
This might sound like a good idea but things will get messy quickly if you want to combine several mixins or you are working with classes that already have an inheritance hierarchy.

Utility Class With Static Methods:
This is the one we are all told not to write but in this context I think it might be the most practical solution. It also has a clear refactoring path path to the above if the project ever moves to Java 8: When default methods in interfaces were introduced, so were static methods in interfaces. The refactoring would first move the static methods and then convert them to non-static.

Some Sort of Design Pattern Involving Composition and Delegation:
This would work. It would also add a lot of boilerplate to the code and complexity to the design. A great strength of mixins in Ruby is the simple way they can be implemented.

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.

0 Comments On This Entry

 

Trackbacks for this entry [ Trackback URL ]

There are no Trackbacks for this entry

October 2017

S M T W T F S
1234567
891011121314
15161718192021
22 232425262728
293031    

Tags

    Recent Entries

    Recent Comments

    Search My Blog

    0 user(s) viewing

    0 Guests
    0 member(s)
    0 anonymous member(s)

    Categories