Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java generics: get class of generic method's return type

Background

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

The current situation

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:

My question

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?

EDIT: Conclusion

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.

like image 212
Peter Jaric Avatar asked Nov 18 '10 10:11

Peter Jaric


People also ask

How do I return a generic class type in Java?

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

How do you find the type of generic type?

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.

Can generics take multiple type parameters?

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.

Can we inherit generic class in Java?

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.


2 Answers

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.

like image 152
Nicolas Avatar answered Sep 21 '22 06:09

Nicolas


#ff0000This 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.

like image 38
Fabrice TIERCELIN Avatar answered Sep 22 '22 06:09

Fabrice TIERCELIN