Duck typing in Java

August 20th, 2007

While the Wikipedia entry on duck typing is quite thorough, Dave Thomas has a much more succinct and simpler definition:

In Ruby, types are defined as the capabilities of objects.

And that is a very interesting definition. Unlike with strong statically-typed languages (such as Java), Ruby doesn’t care about the actual type of the object being passed to a method, as long as it has all the methods that are used in that specific context. Leaving the relative merits and shortcomings of this approach aside for another entry, is this something that can be used in Java? Or, more interestingly, is it something that is used in Java? The answer is quite simple – yes. And not in third-party code; in the JDK itself.

The first example will be the Serializable interface. This is an odd duck (quack quack :). On one hand, it is a marker interface since it has no methods. On the other hand, classes that require special handling during the serialization / deserialization must define implement special methods with these exact signatures:

private void readObject(java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException;
private void writeObject(java.io.ObjectOutputStream stream) throws IOException;
private void readObjectNoData() throws ObjectStreamException;

At runtime, the ObjectStreamClass uses reflection to query the actual object to see if it has these methods. If it does, they are used (once again, using reflection) instead of the default (field by field) behavior. The reason behind having the Serializable interface empty and the method signatures “magic” is, quite frankly, not apparent (you’re welcome to shed more light in the comments). What would happen if these were defined publicly in Serializable (and had empty implementation in a hypothetical SerializableAdapter)? It’s not that suddenly anybody would gain access to the internals of the specific object. Calling writeObject directly is the same as creating an OutputObjectStream and calling writeObject on it. Not only that, even with the current “duck” implementation you can use reflection to query these methods, set them to accessible and call them. A minor annoyance in having all these methods in the Serializable interface would be that you would have to implement all of them if you already extend some other class.

The second example is the UIManager.getUI method. This is where it gets even more complicated than the serialization. To create a UI delegate for a specific component, the implementation first looks up the classname of the UI delegate in the table set up by the current look-and-feel. Then, it uses reflection to call the “magic” static createUI method that must have a single JComponent parameter. This method is then called to create an actual UI delegate. Interestingly enough, this elaborated lookup is not really necessary. The code can be made compile-type safe by using a simple factory that returns UI delegates based on the passed component; just as the look-and-feel fills the lookup table (UIDefaults), it can use the same “switching” to create the UI delegates. The issue with handling third-party component suites such as SwingX or JIDE can be easily addressed by providing an API on the look and feel class that allows installing lookup extensions. This example is a little further from the “pure” duck typing in that it returns a ComponentUI object. This is, of course, not strictly necessary since the relevant code can use reflection to call the methods declared and implemented in this class (which could be quite tiresome, but certainly doable).

There are a few other places throughout the JDK code that use reflection – notably for beans, JMX, a few XML parsers and even the <a href="http://java.sun.com/javase/6/docs/api/java/lang/Class.html">Class</a> class itself (in the <a href="http://java.sun.com/javase/6/docs/api/java/lang/Class.html#getEnumConstants()">getEnumConstants</a>) method. And so, the next time you hear somebody mentioning duck typing, know that this technique is not restricted to dynamically typed languages.