Reputation: 40288
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?
Upvotes: 3
Views: 5240
Reputation: 6084
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.
Upvotes: 4
Reputation: 77995
Maybe you could use something like Javassist to do some meta-programming.
It's used in Tapestry for that purpose, see this article.
Upvotes: 1
Reputation: 988
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
, ...
Upvotes: 3