Thursday, November 13, 2008

Duck Typing in Java using Dynamic Proxies

I have a confession to make: I am a lazy typer. And if there is one thing I can't stand it's having to type the same thing more times than I have to. If you had to pick the most useful thing about Object Oriented languages, it would be the fact that using them results in less typing.  And if you had to pick the one concept in OO that makes that possible, polymorphism would be it.  The idea that you can take objects that represent different concepts and treat them similarly is a powerful one, presenting a lot of opportunities for code reuse that would otherwise be difficult or impossible.

In Java, polymorphism (as in other strongly type languages) is achieved by using inheritance, either by having Classes extend a common base class, or by implementing shared interfaces. This allows the compiler to enforce strict checking about which objects can and can not be used polymorphically.

However, there are a number of downsides to requiring inheritance for polymorphism. Sometimes you may be dealing with legacy classes that you cannot change to extend a base class, or implement a specific interface, requiring you to write wrapper classes or adapters. In other cases there may not be a natural common interface leading you to torture your hierarchy to come up with an artificial one.

In addition, requiring classes to implement a specific interface just so that they can be used polymorphically, is unecessary coupling that represents bad design. One of the lessons learnt from Spring and other container frameworks is that there is virtue in not binding yourself to a specific type hierarchy in order to provide generic functionality.  Rather than having classes which either extend abstract classes or implement a bunch of interfaces, you should be able to just write POJOs and have the framework work out what to do with them.  In Spring we use either annotations, convention, or XML mapping files to tell the framework how to make sense of what a particular POJO is attempting to do.  The virtue of this is that you can get the benefits of
running in Spring, without binding yourself to anything about the Spring container. 

In dynamic languages such as Python and Groovy we are able to take a different approach to polymorphism that avoids some of these problems, using a concept known as Duck Typing. Rather than using the type hierarchy to determine whether we can use classes polymorphically, we just assume that if two methods have the same signature, then they probably mean the same thing, ie. rather than determining whether something IS-A duck, we just care that it WALKS-like-a duck and QUACKS-like-a duck[1].

This concept is best illustrated by an example[2].  Suppose we have the following Groovy code:
class Duck {
def quack() { "a loud Quaaaaaaaaaaaaack!" }
def walk() { "waddle" }
}

class Person {
def quack() { "a person making a quacking sound." }
def walk() { "walk" }
}

def tellAStoryAbout(something) {
println "One day while walking in the forest I heard " + something.quack()
println "Intrigued, I turned round to see a dark shape " + something.walk() +
" off into the bushes."
}

tellAStoryAbout(new Duck())
tellAStoryAbout(new Person())
Which generates the following output:
One day while walking in the forest I heard a loud Quaaaaaaaaaaaaack!
Intrigued, I turned round to see a dark shape waddle off into the bushes.
One day while walking in the forest I heard a person making a quacking sound.
Intrigued, I turned round to see a dark shape walk off into the bushes.

We can see here, that using duck typing (which Groovy supports natively), allows us to treat the two classes Person and Duck polymorphically, without requiring them to be part of the same type hierarchy.  This is nice, because in this particular example, it's hard to think of a really good superclass for both a duck and a person that encapsulates this functionality.

Duck Typing in Java

Being that Java is a strongly typed language, providing this sort of Duck Typing support is not exactly straightforward.  Of course it is possible to use reflection to directly invoke the methods on each of the classes, but this is somewhat cumbersome and inelegant.  Another approach is to specify an interface with the methods you wish to use, and then use a Dynamic Proxy to generate an implementation that wraps the classes you wish to invoke methods on.  This has the benefit of a very compact syntax and the fact that as far as consumers of the Duck-typed objects they are just seeing normal interfaces.

To do this we first create a DuckType class with a static factory method:
public static DuckType coerce(Object object)

and then create a single method which will then allow us to specify the type we
wish to coerce to:
 public  T to(Class iface)
This gives us a rather simple syntax for specifying type coercions. We can then
either then pass in already coerced types to methods, e.g:
static import example.DuckType.*;

interface Foo {
String bar();
}

class Bar {
String bar() { "A man walks into a bar..."; }
}

class FooBar {
String bar() { "foo bar baz qux"; }
}


void method(Foo foo) {
System.out.println(foo.bar());
}

method(coerce(new Bar()).to(Foo.class));
method(coerce(new FooBar()).to(Foo.class));
or alternatively we can just have a method take an Object and perform the type
coercion itself, e.g.
static import example.DuckType.*;

interface Foo {
String bar();
}

class Bar {
String bar() { "A man walks into a bar..."; }
}

class FooBar {
String bar() { "foo bar baz qux"; }
}

void method(Object object) {
System.out.println(coerce(object).to(Foo.class).bar());
}

method(new Bar());
method(new FooBar());
Occasionally it's useful to test up front whether a particular class will support the methods we want to call so for this purpose I've added the following method:
public  boolean quacksLikeA(Class iface)
And that's all there is to it. See Appendix A for the full class listing, but for now let's look at some more useful ways to use Duck Typing with this mechanism.

Useful examples


Okay, so those examples were kind of cheesy; let's try doing something useful with
duck typing.  

Copying a Reader/InputStream


In JDK 1.1, Sun introduced the java.io.Reader and java.io.Writer class to deal
properly with some bugs they had introduced when they had written the InputStream class
to handle character encodings.  Because Sun still hadn't worked out exactly how to use 
interfaces correctly both the InputStream and Reader classes are abstract classes.  Code all over the place mixes and matches Reader and InputStream requiring you to handle both cases.

Now suppose you want to write a simple copy function that will handle either a Reader or InputStream and write output to a Writer.  With duck typing it is easy to do this without having
to handle cases for both classes.  First define the interface with the common method you
want to access:
interface Readable {
int read() throws IOException;
}
Next create your copy function taking an Object as the initial parameter and internally coercing to the Readable interface:
static void copy(Object readerOrInputStream, Writer writer) throws IOException {
int b;
Readable readable = coerce(readerOrInputStream).to(Readable.class);
while ((b = readable.read()) != -1) {
writer.write(b);
}
}


Note that not only will this method now work with InputStream and Reader, but with
any other class that has a read method with the same semantics.

Using annotations polymorphically

Another example where polymorphism is difficult in Java is when using JDK 1.5 Annotations. An annotation is a pseudo-interface that represents meta-data about a class (or elements of a class). However, apart from the implictly extended Annotation interface there is no way to access annotations polymorpically. While this is not a big deal for most people, if you find yourself writing code that processes a lot of annotations, you will find yourself writing a bunch of duplicated code to deal with different annotation types.

However, with Duck Typing it becomes possible to write an annotation processor that
can deal polymorphically with annotations.

Let's suppose we are wanting to write a simple persistence layer for some classes
using annotations. We start by adding the following annotations to classes:

@Persistent
@Immutable
@Transient

We will then create a PersistenceHandler strategy interface that will be used by our annotation processor to work out what to do with these annotations:
interface PersistenceHandler {
void persist(Object object);
}

We define the annotations so they include a default handler implementation.
public @interface Persistent {
Class handler() : default PersistentPersistenceHandler.class;
}

public @interface Immutable {
Class handler() : default ImmutableHandler.class;
}

public @interface Transient {
Class handler() : default TransientPersistenceHandler.class;
}
Next we define the interface we are going to use access the handler attribute of
the annotation:
public interface PersistenceAnnotation {
Class handler();
}

And lastly we write our annotation processor:
void processAnnotations(Object object) throws Exception {
for (Annotation annotation : object.getClass().getAnnotations()) {
DuckType annotationToCoerce = coerce(annotation);
if (annotationToCoerce.quacksLikeA(PersistenceAnnotation.class)) {
Class handler =
annotationToCoerce.to(PersistenceAnnotation.class).handler();
handler.newInstance().persist(object);
}
}
}

The nice thing about this is we have completely decoupled our code from which
particular annotations it can support. We can add new annotations as much as we
like, and providing they support the handler() attribute then we can support them. Also we can add other annotations to our classes that we don't handle and these are simply ignored.

Allowing mocking/testing of legacy classes that don't have interfaces

If you are writing your code using Test Driven Development you'll know about the power of mocking and dependency injection to make your code testable. Using a package such as EasyMock it is very easy to quickly create Mocks/Stubs of classes that would be very difficult to otherwise test.

The way these things work is they generate a Dynamic Proxy for the Interface you want to mock and then allow you to specify your expectations of how that class will be called.

That's all well and good, but it assumes that the things you want to mock are already Interfaces[3] and not concrete classes that you have no ability to make changes over.

Sounds like a problem for Duck typing. Let's use for example the java.io.File class. File has a large number of methods, but typically we'll only want to be using a couple of them. Let's define the interface that we want to use:

interface DeleteableFile {
String getName();
String exists();
boolean delete();
}
Now the method we want to test should be written as:
public deleteTmpFileIfExists(Object fileToDelete) {
DeletableFile file = coerce(fileToDelete).to(DeletableFile.class);
if (file.exists() &&
Pattern.matches("(\\.tmp|~)$", file.getName()) {
file.delete();
}
}
We can now test this with a mock using the DeletableFile interface or a standard
File object.

Creating an encrypted String class

One of the decisions made by the early Sun engineers was to make the String class final. This was done mainly for security reasons that I won't bore you with here, but suffice it to say it makes it very difficult to do a lot of extended String classes that add more functionality. Let's suppose we have an encrypted String class that apes the standard String interface:

public final class EncryptedString {
...
}
By now the process for writing methods that handle both this and a standard String should be fairly obvious.  We create the interface with the methods we care about:
interface StringLike {
boolean startsWith(String prefix);
String subString(int beginIndex);
}


And then we use Duck Typing to refer to either:

String skipLeadingSlash(Object stringLikeThing) {
StringLike s = coerce(stringLikeThing).to(StringLike.class);
if (s.startsWith("/")) {
return s.subString(1);
}
}

Parting words

While Duck Typing is a useful concept, it's not all sweetness and light. You do give up a significant amount of compile-time checking to make the concept work. However, if you are practicing Test Driven Development (and give yourself a slap now if you are not), then with good testing you can make this fact largely irrelevant. What duck typing does give you is an incredibly flexible mechanism for polymorphism that buys out many of the disadvantages of inheritance based approaches. Try it in your own code and see what you can do.

Appendix A: DuckType.java code listing


package example;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
* Allows "duck typing" or dynamic invocation based on method signature rather
* than type hierarchy. In other words, rather than checking whether something
* IS-a duck, check whether it WALKS-like-a duck or QUACKS-like a duck.
*
* To use first use the coerce static method to indicate the object you want to
* do Duck Typing for, then specify an interface to the to method which you want
* to coerce the type to, e.g:
*
* public interface Foo {
* void aMethod();
* }
* class Bar {
* ...
* public void aMethod() { ... }
* ...
* }
* Bar bar = ...;
* Foo foo = DuckType.coerce(bar).to(Foo.class);
* foo.aMethod();
*
*
*/
public class DuckType {

private final Object objectToCoerce;

private DuckType(Object objectToCoerce) {
this.objectToCoerce = objectToCoerce;
}

private class CoercedProxy implements InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Method delegateMethod = findMethodBySignature(method);
assert delegateMethod != null;
return delegateMethod.invoke(DuckType.this.objectToCoerce, args);
}
}

/**
* Specify the duck typed object to coerce.
*
* @param object the object to coerce
* @return
*/
public static DuckType coerce(Object object) {
return new DuckType(object);
}

/**
* Coerce the Duck Typed object to the given interface providing it
* implements all the necessary methods.
*
* @param
* @param iface
* @return an instance of the given interface that wraps the duck typed
* class
* @throws ClassCastException if the object being coerced does not implement
* all the methods in the given interface.
*/
public T to(Class iface) {
assert iface.isInterface() : "cannot coerce object to a class, must be an interface";
if (isA(iface)) {
return iface.cast(objectToCoerce);
}
if (quacksLikeA(iface)) {
return generateProxy(iface);
}
throw new ClassCastException("Could not coerce object of type "
+ objectToCoerce.getClass() + " to " + iface);
}

private boolean isA(Class iface) {
return objectToCoerce.getClass().isInstance(iface);
}

/**
* Determine whether the duck typed object can be used with
* the given interface.
*
* @param Type of the interface to check.
* @param iface Interface class to check
* @return true if the object will support all the methods in the
* interface, false otherwise.
*/
public boolean quacksLikeA(Class iface) {
for (Method method : iface.getMethods()) {
if (findMethodBySignature(method) == null) {
return false;
}
}
return true;
}

@SuppressWarnings("unchecked")
private T generateProxy(Class iface) {
return (T) Proxy.newProxyInstance(iface.getClassLoader(),
new Class[] { iface }, new CoercedProxy());
}

private Method findMethodBySignature(Method method) {
try {
return objectToCoerce.getClass().getMethod(method.getName(),
method.getParameterTypes());
} catch (NoSuchMethodException e) {
return null;
}
}

}

Footnotes


[1] Attributed to Alex Martelli in a message to the comp.lang.python newsgroup [Wikipedia].
[2] Adapted from the example in the [Wikipedia Duck Typing Article.
[3] More recent versions of EasyMock do class instrumentation to overcome this limitation, but we'll ignore that for now.