Reputation: 12861
I just learned about this fine looking syntax
Collections.<String>emptyList()
to get an empty List
with elements which are supposedly of type String
. Java's source looks like this:
public static final List EMPTY_LIST = new EmptyList<Object>();
:
public static final <T> List<T> emptyList() {
return (List<T>) EMPTY_LIST;
}
Now if I code a method in that way where the generic type does not appear in the parameter list, is there any way how I can access the actual class that becomes T
?
I'm saying, up to now my approach to code the same thing would have been
private <T> T get(String key, Class<T> clazz) {
// here I can do whatever I want with clazz, e.g.:
return clazz.cast(value);
}
If I removed the clazz
-parameter I wouldn't be able to do the cast()
. Obviously I could do
return (T) value;
but that gives me the usual warning Type safety: Unchecked cast from Object to T
. Ok, @SuppressWarnings("unchecked")
helps here, but actually I want to do something with the intended return type of the method. If I add a local variable
T retValue;
I'd have to initialise it with something, null
doesn't help. After I assign it like
@SuppressWarnings("unchecked")
T retValue = (T) value;
I could do, e.g.
retValue.getClass().getName()
but if the cast fails I end up with no information about T
again.
Since Java (or at least my Java 6) does not have the generic info any more during runtime, I currently can't think of a way to do this. Is there a way? Or do I have to stick with my "old" approach here?
Please note that the example I lined out is very simple and doesn't make much sense. I want to do more complicated stuff here, but that's out of the scope.
Upvotes: 9
Views: 7521
Reputation: 1
I find out that there is one solution for getting Class<?>
from T:
public class Action<T>{
}
public Class<?> getGenericType(Object action) throws ClassNotFoundException{
Type type =
((ParameterizedType)action.getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
String sType[] = type.toString().split(" ");
if(sType.length != 2)
throw new ClassNotFoundException();
return Class.forName(sType[1]);
}
The usage of code above:
Action<String> myAction = new Action<String>();
getGenericType(myAction);
I did not tested this with primitive types (int, byte, boolean).
I think that it is not very fast, but you do not have to pass Class<?>
to constructor.
EDIT:
The usage above is not right, because generic superclass is not available for Action<String>
. Generic super class will be available only for inherited class like class ActionWithParam extends Action<String>{}
. This is reason why I changed my mind and now I suggest to pass class parameter to constructor, too. Thanks to newacct for correction.
Upvotes: -2
Reputation: 533492
If you want the generic type at runtime you need to either have it as a field or create a sub-class of a type for a specific combination of types.
e.g.
List<String> list = new ArrayList<String>() {}; // creates a generic sub-type
final Class type = (Class) ((ParameterizedType) list.getClass()
.getGenericSuperclass()).getActualTypeArguments()[0];
System.out.println(type);
prints
class java.lang.String
Upvotes: 6
Reputation: 49744
retValue.getClass().getName()
will always return the runtime type of the object and not the class name of the parameter type.
If you want to grab the parameter class, there's no other way than to use your first solution. (That is, pass the class object explicitly.)
Upvotes: 2
Reputation: 3509
You can't, unfortunately. All generics and type parameters are erased in runtime. So in runtime the type of your T
is simply Object
Upvotes: 4
Reputation: 25139
As you mentioned, Java generics are build time only. They are not used at run time.
Because of this, the old approach you were using will be your only way to accomplish this.
Upvotes: 1