Tuesday, October 5, 2010

Discover type of parametrized class

Introduction

Generics is a beautiful feature of Java 1.5. But it has some limitations. One of them is that it is impossible to discover type of parametrized class. For example if you have generic code like:
List<?> mylist = getList();
where getList() is can return either List<String> or List<Integer> there is not convenient way to discover it. Class Class and reflection API do not provide ability to discover the type of parameter. The reason is simple: generics is the compiler’s feature. The class’ parameters are not stored in .class file and therefore cannot be accessed at runtime. Sometimes we can use workarounds. For example in case of list we can retrieve the first element and then call getClass(). Obviously this method will not work for empty lists and for null elements.

Use abstract class

Other workaround exists if we develop both sides: the discovered class and the code that discovers it. Assume that we have interface Foo:

interface Foo<T> {
    void foo(T t);
}
Assume also that we want to implement framework that deals with instances of classes implementing Foo and this framework should know the concrete type of T. One solution is to add special method that returns T.

interface Foo<T> {
    void foo(T t);
    T getType();
}
Each class that implements Foo must now implement 2 methods. I think that this design may be improved. We can move this functionality to abstract class:

public abstract class AbstractFoo<T> implements Foo<T> {
    private Class<T> t;

    protected AbstractFoo(Class<T> t) {
        this.t = t;
    }

    public Class<T> getType() {
        return t;
    }
}
The abstract class defines protected constructor that receives parameter of type Class, so all its sub classes must call it.

public class FooImpl extends AbstractFoo<String> {
    public FooImpl() {
        super(String.class);
    }

    public void foo(String s) {
        // the implementation...
    }
}
I think that this approach is better than implementing getType() in each sub-class separately because sub-class is dedicated on its own task while abstract class cares cares to provide information about the type to framework. Constructor is typically written in the beginning of the class, i.e. near the definition of class itself and therefore it is easier to see mistakes.

Real example

Here is more complicated example. Several year ago I worked for small start up company named NLayers that implemented network discovery solution. We connected to remote devices using various protocols that were represented in application by hierarchy of interfaces extended from top level interface Session. Logic of discovery itself was implemented in relatively small classes we called “probes.” All these classes implemented interface Probe. The framework decided which probe should be executed and when. The framework should know the Session type supported by probe. There were many types of session and parallel hierarchy of abstract probes: ShellSession and ShellProbe, SnmpSession and SnmpProbe etc. The years passed since that time and now I’d like to suggest improved design with only one AbstractProbe. The design uses ideas described above. The code snippets here are not stolen from my former employer but written yesterday from scratch. Real code was more complicated and contained more details irrelevant for this example.

Acknowledgments

I wold like to thank my former colleagues at NLayers: Avshi Avital, Oran Epelbaum, Chaim Linhart, Yariv Bandiel, David Resnik. I enjoyed working with them and develop product that gave me idea for this article.

No comments:

Post a Comment