Multiple bounds in Java 5 generics

30 April 2006, 10:12

I'm doing some custom rendering of JasperReports designs, this is part of the domain model I'm working with:

There are more subclasses of JRDesignGraphicElement, but not all of them can have a box drawn around them, only the ones in the diagram are 'boxable'. Being boxable as an element adds some extra properties to that element, for example the width of the leftborder or the padding (I omitted most of the properties in the diagram for brevity). The way it is implemented in JasperReports is a bit unhappy (more about that later), but since it is an external library I have to deal with the existing domain model.

So far so good, the problem starts when I want to write a method that takes a Jasper element and draw a border around it. Something like this would seem right:
void drawBorder(Graphics2D gg, JRBox box)

The problem with this approach is that to be able to draw a border around an element, I need also the coordinates of that element and they are not exposed through the JRBox interface, but are defined in the JRDesignElement class. Bummer. I could cast it to a JRDesignElement though when I need the x, y, width and height attributes, and that probably wouldn't be a problem, as there are no other classes which implement the JRBox interface that are used in my code. But doing that would give me an icky feeling. It would create an unchecked (at compile time) precondition for using this method, and since I'm using a compiler anyway, I'd better use it, right?

The following declaration solves the problem:
<E extends JRDesignElement & JRbox> void drawBorder(Graphics2D gg, E e)

What this is saying is that E is of type JRDesignElement and of type JRBox, effectively letting me use methods and properties of both types.

e.getX(); and e.getLeftBorder()

is legal code inside the method declaration, and the compiler will enforce that I don't pass on any objects of type JRDesignElement that don't implement the interface JRBox to the method. Pretty sweet stuff I think. You can even specify more bounds if you want like E extends N & N1 & N2 & .... One remark though, Eclipse 3.1.2 can't seem to handle multiple bounds, the code compiles, but code completion doesn't show all the available methods and sometimes the background compiler shows errors that dissapear after compiling the project. IntelliJ has no problems with this. (of course:))

I mentioned before that I think the JRBox functionality is a bit unhappily implemented, actually I had been thinking about this on the way home the day I wrote the drawBorder method, but couldn't find a better solution myself. The JRBox interface is inconvenient first of all because of the problem laid out before, but also because of the fact that every class that implements JRBox has the same tedious implementation (== code duplication). The easy way would be to create an abstract class JRBox that inherits from JRDesignGraphicElement and let all classes that are boxable inherit from JRBox. Technically speaking this would be a good solution but it would violate some OO principles. Elements can be boxable, which implies a 'flavor' of an element, but doesn't mean they are a box.

How else would you implement this? Any suggestions?

In a more versatile language like Ruby we have mixins which are perfect for this situation. The implementation could look like this:

module JRBox
	def left_padding
		...
	end
	
	def left_border
		...
	end
end

class JRDesignElement
  attr_accessor :x, :y, :width, :height
end

class JRDesignGraphicElement < JRDesignElement
end

class JRDesignImage < JRDesignGraphicElement
	include JRBox
end

class JRDesignTextElement < JRDesignGraphicElement
	include JRBox
end

Writing the method drawBorder wouldn't be a problem either, because of 'duck typing'.
def drawBorder(design_element)
would be enough. As long as the type of design_element has the methods and properties we use in the method the world keeps turning.

I looked a bit around for mixin support in Java, and the closest I could find was the rapt project. I implemented the same domain model using rapt, but the code became so ugly I didn't even compile it (you have to compile with apt, not with javac which would also require some java command line skills which are somewhere in the back of my skull beneath a deep layer of IDE-using brain cells).

Some thoughts on Team System this.setDraft(false) or this.post()