I once wrote this method:
private <T> SortedSet<T> createSortedSet() {
return new TreeSet<T>();
}
It's supposed to be called like this:
Set<String> set = createSortedSet();
This works fine (although I've seen in answers here when researching the current question that this is error-prone).
Anyway, now I am writing the following code (in a class that extends javax.servlet.jsp.tagext.TagSupport):
private <T> T evaluate(String expression) {
ExpressionEvaluator evaluator = pageContext.getExpressionEvaluator();
return evaluator.evaluate(expression, T, null, pageContext.getVariableResolver());
}
The purpose is to be able to call this like:
Integer width = evaluate(widthStr);
The code in my evaluate method obviously doesn't work. The second argument to evaluator.evaluate()
is supposed to be a Class object. This leads me to:
How can I get the Class of a generic (return) type? What should I write in place of T as the second argument to evaluate?
Nicolas seems to be right, it can not be done and I need to pass the class as an argument to my method. The upside is that since his solution makes the argument parametrized on the generic type I get a compilation check on that argument.
(Yes, this is legal code; see Java Generics: Generic type defined as return type only.) The return type will be inferred from the caller. However, note the @SuppressWarnings annotation: that tells you that this code isn't typesafe. You have to verify it yourself, or you could get ClassCastExceptions at runtime.
Use the IsGenericType property to determine whether the type is generic, and use the IsGenericTypeDefinition property to determine whether the type is a generic type definition. Get an array that contains the generic type arguments, using the GetGenericArguments method.
Multiple parametersYou can also use more than one type parameter in generics in Java, you just need to pass specify another type parameter in the angle brackets separated by comma.
Generics also provide type safety (ensuring that an operation is being performed on the right type of data before executing that operation). Hierarchical classifications are allowed by Inheritance. Superclass is a class that is inherited. The subclass is a class that does inherit.
Unfortunately, you will certainly have to change your method to:
private <T> T evaluate(Class<T> clazz, String expression)
and then pass clazz
as your second parameter. Not as short as you expected.
This doesn't work with all JVM!
You can first create a generic object and then retrieve its parameterized type:
private <T> T evaluate(String expression) {
List<T> dummy = new ArrayList<>(0);
Type[] actualTypeArguments = ((ParameterizedType) dummy.getClass().getGenericSuperclass()).getActualTypeArguments();
Type clazz = actualTypeArguments[0];
Class<T> theClass = (Class<T>) clazz.getClass();
ExpressionEvaluator evaluator = pageContext.getExpressionEvaluator();
return evaluator.evaluate(expression, theClass, null, pageContext.getVariableResolver());
}
Beware! You don't get a Class<>
object, you get a TypeVariableImpl
subclass object, which may behave differently.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With