Run-time type checking with Java Generics

I've been looking at the Apache XML-RPC library, and figuring out how best to marshal and unmarshal Java objects that I'm passing between the client and server. XML-RPC only supports a limited set of data types, so if you want to pass the contents of an object across the interface you need to convert the object before transmitting it, the normal method being to transform it into an XML-RPC struct, which is a key-value hash map. The Apache library uses a Java Map to represent the XML-RPC struct type, with the keys being String objects and the values being any valid Java object.

Because I'm paranoid, I want to check the data types of the values as I convert the incoming XML-RPC request back into the corresponding Java objects. I could do this by just casting to the appropriate type and catching any ClassCastException, something like this:

    Person p;
    try {
        String name = (String) map.get("name");
        int age = (int) map.get("age");
        p = new Person(name, age);
    } catch (ClassCastException e) {
        System.out.println("Bad parameter type");
    }

but the diagnostics are not exactly useful, especially where there are a lot of entries in the map. The other alternative is to have a method that checks the type of the value retrieved from the map, and that throws an exception with a helpful diagnostic if there is a mismatch. However, I'd either have to make the checking method return Object and then cast it to the right type, or have a whole set of methods for each type I was interested in, e.g. checkInteger, checkString and so forth, which would be a pain to have to write.

However, it occurred to me that Java Generics might provide a better way of doing this. The most excellent Java Generics FAQ gave me a clue as how best to do this - I needed to provide the class I wanted to check against as a Class parameter to the type checking method. And - the really neat bit - by parameterising the Class argument we can then use the same type for the return type of the method, thus dispensing with the need to cast the return type of the method when we call it.

    public static <T> T typeCheck(Object obj, Class<T> type, String name)
      throws ClassCastException {
        Class objClass = obj.getClass();
        if (! type.isAssignableFrom(objClass)) {
            throw new ClassCastException("invalid type " +
              objClass.getSimpleName() + " for " + name + ", should be " +
              type.getSimpleName());
        }
        return type.cast(obj);
    }

With this in place, we can check and convert the type of an object without needing an explicit cast:

    Object obj = map.get("name");
    String name = DataMarshaler.typeCheck(obj, String.class, "name");

In the final version I wrapped up the map retrieval and type conversion into a single method, but it was clearer to show the type conversion as a seperate step in the example above. This is really kind of neat, however it's not entirely perfect - you can't pass in a parameterised type as the class argument, i.e.

    Map<String, Integer> = DataMarshaler.typeCheck(obj, Map<String, Integer>.class, "map");

because parametrised types are not themselves first-class entities in the Java type system, as a consequence of type erasure.

Categories : Java, Tech, Work