views:

468

answers:

3

So, the group I work with has reduced the amount of code we have to type for certain things. In this case, a Spring web page that displays a list using the DisplayTag libraries. The way it's done is with a class using generics extending the Controller object, and then a subclass of that for each page it should work on.

This controller displays the SystemErrorReport, and defines the type to be the SystemErrorReportCommand.

public class SystemErrorReportController extends
    GenericSearchAndSelectController<SystemErrorReportCommand> {

The problem is that SystemErrorReportCommand, being passed as a type, needs to have a manual declaration of itself in its constructor, like so:

public SystemErrorReportCommand()
{
    super(SystemErrorReport.class);
}

The command object is later passed to something that needs to know its explicit type. Without specifying it manually somewhere, it comes back as GenericCommand, another class of ours, because the SystemErrorReportCommand bit is lost after compile time.

I'm not thrilled with that, because it seems like something we can farther automate to reduce developer error. Is there a more elegant way to do this, or am I stuck with this because of type erasure?

+1  A: 

Even after erasure has done its job, it is still possible to retrieve generic information from: Field, Method ( including method parameters ) and Class using reflection.

For example, it is possible to retrieve the SystemErrorReportCommand from SystemErrorReportController using

((ParameterizedType) SystemErrorReportController.class.getGenericSuperType()).getActualTypeArguments[0];

Still, that's only the beginning. You need to start making use of that information using reflection api, so it is likely that your design will suffer if you were not expecting something like this.

Here is a little example of what you can extract at runtime:

public abstract class Test<T extends Number> extends ArrayList<Long> implements Comparable<String>, List<Long>{
    public List<String> field;
    public abstract <M> M method(List<T> arg);

    public static void main(String... args) throws Exception {
        TypeVariable<Class<Test>> t = Test.class.getTypeParameters()[0];
        Class<?> upperBound = (Class<?>) t.getBounds()[0];


        System.out.println("Test<" + t + " extends " + upperBound.getName() + ">");
        System.out.println("extends " + Test.class.getGenericSuperclass());
        System.out.println("implements " + Arrays.toString(Test.class.getGenericInterfaces()));
        System.out.println("{");
        System.out.println(Test.class.getMethods()[1].toGenericString());
        System.out.println(Test.class.getFields()[0].toGenericString());
        System.out.println("}");
    }
}

That will output:

Test<T extends java.lang.Number>
extends java.util.ArrayList<java.lang.Long>
implements [java.lang.Comparable<java.lang.String>, java.util.List<java.lang.Long>]
{
public abstract <M> M Test.method(java.util.List<T>)
public java.util.List<java.lang.String> Test.field
}

I'm using toGenericString() method. However this is just a pretty printing method ! There is no need to parse that String: reflection api provides all the necessary information, eg: Field.getGenericType(), Method.getArgumentTypes, ...

vdr
Let me check this on Monday. We're already using quite a bit of reflection, I may need to be more specific with my question, but don't want to misspeak without the example in front of me.
Dean J
Okay, checked.The problem here is that there's something like:class Foo<Bar>;-and-class Baz extends Bar;-and-class Zab extends Bar;When something calls Foo<Bar> with Baz (Bar's subclass), all I can get back is Bar, not Baz; I've lost the subclass and can only get back the explicit type.Did that make any sense?
Dean J
Yep, unfortunatly, that's the kind of info that is erased. At runtime, there is only 1 instance of the class Foo (Foo.class). So you only get the "declared" generic type rather than the "instance" generic type. This is what "reified generic" discussion (http://gafter.blogspot.com/2006/11/reified-generics-for-java.html) is about. Unfortunatly that won't be part of java 7 either.
vdr
+1  A: 

Take a look at Super Type Tokens ..seems like that should work for you in this case .

Another link

Surya
Interesting to hear that erasing erasure might be part of Java 7.
Dean J
+1  A: 

Maybe you could use something like Javassist to do some meta-programming.

It's used in Tapestry for that purpose, see this article.

JRL