Monday, March 17, 2008

Are you fully using your Static Typing ?

Back in 2005 in an LtU discussion on Dynamic vs. Static Typing, Anton van Straaten had this post ..

Here's a nice bit of Java code I came across (here):


if ((value != null) && !returnedClass().isAssignableFrom(value.getClass())) {
    throw new IllegalArgumentException("Received value is not a [" +
       returnedClass().getName() + "] but [" + value.getClass() + "]");
}



This is from a piece of code that's trying very, very hard to avoid the need for the definition of boilerplate classes when persisting classes representing enumeration types to a SQL database.

This code is actually doing a kind of dynamic typechecking, illustrating the following generalization of Greenspun's 10th Law: "any sufficiently complicated program in a statically-typechecked language contains an ad-hoc, informally-specified bug-ridden slow implementation of a dynamically-checked language." ;)

Today's good Java frameworks use reflection quite sparingly and responsibly. Using Java generics, these frameworks allow compile time type checking for cases which would earlier have to be implemented using a slow and bug ridden simulation of runtime type checking. Guice and EasyMock stand out as two frameworks I have been using that have used the power of generics to implement extraordinary typesafety.

I really like the way small interface-heavy APIs of Guice enforce compile time type-safety.

Have a look at this piece of code, which binds an implementation SpecialServiceImpl to the interface Service using Guice Binder.


public class MyModule implements Module {
    public void configure(Binder binder) {
        binder.bind(Service.class)
              .to(SpecialServiceImpl.class)
              .in(Scopes.SINGLETON);
    }
}



Given the fact that DI frameworks are in the business of injecting implementations to objects dynamically, it may seem that the "implements" relationship between Service and SpecialServiceImpl is done during runtime. Thanks to Java generics, every bit of type checking is done during compile time.

A peek at the source code of Guice reveals that BinderImpl.bind() returns BindingBuilderImpl<T> ..


public <T> BindingBuilderImpl<T> bind(Class<T> clazz) {
    return bind(Key.get(clazz));
}



and BindingBuilderImpl<T>.to() takes as input Class<? extends T> - the bound on the wild card enforces the above "implements" relationship as part of compile time type checking of the arguments ..


public ScopedBindingBuilder to(Class<? extends T> implementation) {
    return to(TypeLiteral.get(implementation));
}



In comparison to Guice, Spring makes much heavier use of reflection, which, I think, is, kind of expected, from a pre-generics framework. Spring's implementation has lots of code similar to


// Check if required type matches the type of the actual bean instance.
if (requiredType != null && bean != null &&
      !requiredType.isAssignableFrom(bean.getClass())) {
    throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}



that does quite a bit of juggling with dynamic type checking at runtime.

Coming back to the above post by Anton, yes, the kind of runtime type checking exists in lots of popular Java frameworks, even today. And this is where frameworks like Guice and EasyMock really shine with their strongly typed API sets that make you feel more secure within the confines of your IDE and refactoring abilities.

The Morale

When you are programming in a statically typed language, use appropriate language features to make most of your type checking at compile time. This way, before you hit the run button, you can be assured that your code is well-formed within the bounds of the type system. And you have the power of easier refactoring and cleaner evolution of your codebase.

4 comments:

Anonymous said...

Dynamic typechecking is either an anti-pattern in dynamics language.

You should avoid run time type checking in almost every language.

http://theplana.wordpress.com/2008/03/09/ruby-idioms-avoid-check-class-membership/

Debasish said...

@Anonymous:
In dynamically typed languages, the programmer does not have to do any dynamic type checking. It is done by the compiler / interpreter, since types are not explicitly specified by the programmer. However, in statically typed languages like Java, it does not make sense to do likewise, since you can enforce static typesafety during compilation itself.

Alan Burlison said...

One technique I haven't seen discussed much is the use of generic type information to do type checking and inferencing a run-time. Despite type erasure, classes and methods retain a useful proportion of their generic information at run-time. The Stripes framework uses it, and a project I am working on at the moment also uses it.

An example might make this clearer: I'm using reflection on generics to automatically map arbitrary Java types to those needed for transmission across XMLRPC. For example, given a method with a return type of List<Map<String, Date>>, I can map that automatically onto the corresponding XMLRPC equivalent, an array containing structs which in turn have strings as the keys and dates as the values. This technique can be used to map both arguments and return types, so from an interface definition it is possible to completely define the corresponding XMLRPC function - and of course, implement it as well.

Anonymous said...

returnedClass().isAssignableFrom(value.getClass())

should be written as

returnedClass().isInstance(value)