简体   繁体   中英

Getting around Type Erasure in Java

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?

Contrary to what is widely accepted and rarely known type erasure can be avoided, which means that the callee do have the ability to know which generic parameters were employed during the call.

Please have a look at: Using TypeTokens to retrieve generic parameters

Thanks

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 , ...

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM