I have a generic class, says :
MyClass<T>
Inside a method of this class, I would like to test the type of T, for example :
void MyMethod()
{
if (T == String)
...
if (T == int)
...
}
how can I do that ?
Thanks for your help
You can't, normally, due to type erasure. See Angelika Langer's Java Generics FAQ for more details.
What you can do is pass a Class<T> into your constructor, and then check that:
public MyClass<T>
{
private final Class<T> clazz;
public MyClass(Class<T> clazz)
{
this.clazz = clazz;
}
public void myMethod()
{
if (clazz == String.class)
{
...
}
}
}
Note that Java does not allow primitives to be used for type arguments though, so int is out...
Because of type erasure you can't... mostly. But there is one exception to that. Consider:
class A {
List<String> list;
}
public class Main {
public static void main(String args[]) {
for (Field field : A.class.getDeclaredFields()) {
System.out.printf("%s: %s%n", field.getName(), field.getGenericType());
}
}
}
Output:
list: java.util.List<java.lang.String>
If you need the class object, this is how you generally handle it:
public <T> T createObject(Class<T> clazz) {
return clazz.newInstance();
}
ie by passing the class object around and deriving the generic type from that class.
Additionally to cletus one exception I've mine: super type tokens. The super type token will preserve the type information.
new Type<Set<Integer>>() {}
The type information can be retrieved with Class.getGenericSuperClass.
if (object instanceof String)
System.out.println("object is a string");
As it was already stated you can get only generics-related information available at the static byte code level.
It's possible to resolve type arguments values and check if one type may be used in place of another then.
if you have subclass B extends A that should match, too, the approach clazz == A.class. Doesn't work. You should then use A.class.isInstance(b) where b is an object of type B.
If you want to do different things for different types would it still be generic?
Related
How can I convert java.lang.reflect.Type to Class<T> clazz?
If I have one method as next which has an argument of Class<T>:
public void oneMethod(Class<T> clazz) {
//Impl
}
Then another method which has an argument of java.lang.reflect.Type and it calls oneMethod(Class<T> clazz) and for it I need to convert java.lang.reflect.Type type to Class<T>:
public void someMehtod(java.lang.reflect.Type type) {
// I want to pass type arg to other method converted in Class<T>
otherMethod(¿How to convert java.lang.reflect.Type to Class<T>?);
}
Is it possible?
You have to ensure that type is an instance of Class, and then cast it.
if (type instanceof Class) {
Class<?> clazz = (Class<?>) type;
otherMethod(clazz);
}
Of course, you also have to handle the case of it not being a Class.
If you are willing to use a library, you could use com.google.guava:guava:12+:
Class<?> clazz = com.google.common.reflect.TypeToken.of(type).getRawType();
Alternatively you could also use com.fasterxml.jackson.core:jackson-databind:2.8.x:
Class<?> clazz = com.fasterxml.jackson.databind.type.TypeFactory.rawClass(type);
This handles all cases correctly and you will get the type-erased class of your type.
Using generic types in runtime is a little bit tricky in Java. And I think this is a root cause of your issue.
1) to be sure about generic in runtime we doing like this:
class MyClass<E> {}
and then:
MyClass<TargetType> genericAwaredMyClassInctance = new MyClass<TargetType>(){};
please pay attention to {} in the end. It means anonymous extends of MyClass. This is an important nuance.
2) let`s improve MyClass to be able to extract the type in runtime.
class MyClass<E> {
#SuppressWarnings("unchecked")
protected Class<E> getGenericClass() throws ClassNotFoundException {
Type mySuperclass = getClass().getGenericSuperclass();
Type tType = ((ParameterizedType)mySuperclass).getActualTypeArguments()[0];
String className = tType.getTypeName();
return (Class<E>) Class.forName(className);
}
}
and finally, use it like this:
MyClass<TargetType> genericAwaredMyClassInctance = new MyClass<TargetType>(){};
assert(genericAwaredMyClassInctance.getGenericClass() == TargetType.class)
Andy Turner answer is correct, however if you need to retrieve a class from a type parameter this is a complete example:
private static class MyClass extends ArrayList<Integer> {
}
public static void main(String[] args) {
ParameterizedType arrayListWithParamType
= (ParameterizedType) MyClass.class.getGenericSuperclass();
Type integerType = arrayListWithParamType.getActualTypeArguments()[0];
Class<?> integerClass = (Class<?>) integerType;
System.out.println(integerClass == Integer.class);
}
It would be weird that a Type would be anything else than a Class... Javadoc for Type says
All Known Implementing Classes:
Class
So unless you have special libraries that use non Class Types, you can simply cast - but you must be ready for a possible ClassCastException.
Beware: Java use undocumented Type implementation to represent generics, see below.
You can explicitely process it or not because it is an unchecked exception:
Explicit way:
try {
Class<?> clazz = (Class<?>) type;
}
catch (ClassCastException ex) {
// process exception
}
Implicit way:
Class<?> clazz = (Class<?>) type;
but the current method could throw...
EDIT per #Andy Turner's comment:
Beware: Type type = new ArrayList<String>().getClass().getGenericSuperclass(); yields something that's a Type but not a Class. This one is a ParameterizedType, so you can use getRawType() method to find the actual class, but others might exist.
Did you mean this?
public <T extends Type> void oneMethod(T clazz) {
}
public void someMethod(Type type) {
oneMethod(type);
}
If I have the following interface:
interface MyElementProcessor<T> {
void doSomethingWith(T element);
Class<T> getElementClass();
/* more functions... */
}
Say that I want to implement this for a generic type like Collection<T>. How do I implement getElementClass()? My best attempt so far is something like this:
class MyCollectionProcessor<T> implements MyElementProcessor<Collection<T>> {
public void doSomethingWith(Collection<T> element) { /* ... */ }
public Class<Collection<T>> getElementClass() {
// Ugly, but the only way I have found to cast to Class<Collection<T>>:
return (Class<Collection<T>>) (Object) Collection.class;
}
}
I know that there is no way to specify a Class<Collection<T>> literal in Java, but why isn't it possible to cast Collection.class directly to Class<Collection<T>>? Is there a better way than casting via Object?
why isn't it possible to cast Collection.class directly to
Class<Collection<T>>?
Collection.class has type Class<Collection>. Java will not compile this type cast because it can be proved that it cannot succeed. There is probably no type that is a subtype of both Class<Collection> and Class<Collection<T>>.
In Generics, Foo<A> is never a subtype of Foo<B> if A and B are different and they are types (not wildcards), regardless of the relationship between A and B. Therefore, the only subtypes of Class<Collection> are SubclassOfClass<Collection>. And similarly the only subtypes of Class<Collection<T>> are SubclassOfClass<Collection<T>>. Since these do not intersect, there is no way this cast can theoretically succeed.
Is there a better way than casting via Object?
You could cast via Class<?>; but it is not much better:
return (Class<Collection<T>>) (Class<?>) Collection.class;
You could pass the class literal to the constructor:
private final Class<Collection<T>> clazz;
public MyCollectionProcessor(Class<Collection<T>> clazz) {
this.clazz = clazz;
}
public Class<Collection<T>> getElementClass() {
return clazz;
}
No cast needed but an extra parameter...
Because of Java generics handling, a Class<List<Integer>> class cannot be obtained (without unchecked cast), but will be Class<List> or Class<List<?>>.
Therefore, if you're working with parameterized type in your ElementProcessor, i suggest that you change your method to be Class<? super T> getElementClass()
Two examples :
interface MyElementProcessor<T> {
void doSomethingWith(T element);
Class<? super T> getElementClass();
/* more functions... */
}
SimpleElementProcessor implements MyElementProcessor<Integer> {
public void doSomethingWith(Collection<Integer> element) { /* ... */ }
public Class<Integer> getElementsClass() {
return Integer.class;
}
}
CollectionElementProcessor<E> implements MyElementProcessor<Collection<E>> {
public void doSomethingWith(Collection<Collection<E>> element) { /* ... */ }
// This works because Class<Collection<?>> is a valid substitute for Class<? super T>
public Class<Collection<?>> getElementsClass() {
return (Class<Collection<?>>) Collection.class; // Maybe the cast isn't needed
}
}
As for obtaining the elements class, you can use reflection : If your type derives MyElementProcessor you can obtain it like this :
for(Type interface : getClass().getGenericInterfaces()) {
if(interface instanceof ParameterizedType && ((ParameterizedType) interface).getRawType == MyElementProcessor.class)
Type myElementsType = ((ParameterizedType) interface).getActualTypeArguments()[0];
This only works for deriving classes, that is, anonymous classes or declared types, it won't work if you use it this way because of type erasure( this example is dummy : it is only an example) :
public <T> MyElementProcessor newInstance() {
return new MyElementProcessor<T> {
// overridden methods ...
};
}
In such a case, you will either :
Not obtain a ParameterizedType but rather directly the MyElementProcessor.class, whose type argument is a TypeVariable and does not provide you with it's actual type implementation
Obtain a ParameterizedType whose raw type is MyElementProcessor and actual type argument be a TypeVariable
How can I get the "real" class of a generic type?
For Example:
public class MyClass<T> {
public void method(){
//something
System.out.println(T.class) //causes a compile error, I wont the class name
//something
}
}
If T = Integer
Output:
java.lang.Integer
If T = String
Output:
java.lang.String
Thanks
If you have a instance variable of type T in your class, and it happens to be set, then you could print the class of that variable.
public class Test<T> {
T var;
public static void main(String[] args) {
Test<Integer> a = new Test<Integer>();
System.out.println(a.boo());
a.setVar(new Integer(10));
System.out.println(a.boo());
}
public String boo() {
if (var == null) {
return "Don't know yet";
}
return var.getClass().getSimpleName();
}
public void setVar(T var) {
this.var = var;
}
public T getVar() {
return var;
}
}
You can't. The information is stripped from the code at compile time, a process that is known as type erasure. For more, please look here: Type Erasure
edit: sorry my bad, the information is not loaded at run time.
As others have explained, you cannot do it in that fashion but this is how it's usually achieved in java.
public class MyClass<T> {
public void method(Class<T> clazz) {
// something
System.out.println(clazz.getName());
// something
}
}
and you use it like this
new MyClass<String>().method(String.class);
In the case of your situation, you can't. However, you might be able to use Super Type Tokens for this type of thing: http://gafter.blogspot.com/2006/12/super-type-tokens.html
An example implementation of these is the TypeReference class of the Jackson json processing library.
This is advanced stuff and probably more than you wanted to know ;-)
Note that approaches relying on 'getClass()' on an instance received with a generic type will get the actual type of that object, which is not necessarily the generic type - which would be the type by which the caller knew the instance.
For example, consider the case where the caller handles an object by an interface; when passing to generic constructs, the generic type will be the interface, not the instance's actual class.
Consider the following example "Pair" class, which allows two object references to be returned through a POJO:
public class Pair<U,V>
{
public final U first;
public final V second;
public static <U,V> Pair<U,V> of (U first, V second)
{
return new Pair<U,V> (first, second);
}
protected Pair (U first, V second)
{
this.first = first;
this.second = second;
}
}
We were considering how to modify the 'Pair.of()' factory function to return a Comparable Pair derived class, if U and V were both Comparable. However, while we can tell whether 'first' and 'second' are comparable using instanceof, we don't know that 'U' and 'V' are themselves comparable.
For this to work, the exact type of Pair returned by Pair.of() must depend on the generic types, not the actual argument types.
Isn't there any way to find the class-type of a generic?
if (T instanceof String) {
// do something...
}
The above definitely does not compile.
Generics are a compile time feature. Generics add checks at compile time which may not have any meaning at runtime. This is one example. You can only check the type of the object referenced which could be a super type in code. If you want to pass the type T you have do this explicitly.
void someMethod(Class<T> tClass) {
if(String.class.isAssignableFrom(tClass))
or
void someMethod(Class<T> tClass, T tArg) {
Note: the type might not be the same,
someMethod(Number.class, 1);
It won't compile because T is not a variable, but a place holder for a class that is defined at runtime. Here's a quick sample:
public class Test<T> {
public void something(T arg) {
if (arg instanceof String) {
System.out.println("Woot!");
}
}
public static void main(String[] args) {
Test<String> t = new Test<String>();
t.something("Hello");
}
}
if you have subclass
public class SomeClass extends SomeSubclass<String>{}
and
public class SomeSubclass<T> {}
then there is a way to discover type of T by executing code
Type t = getClass().getGenericSuperclass()
if (t instanceof ParameterizedType) {
Type[] actualTypeArguments = ((ParameterizedType)t).getActualTypeArguments()
// in simple cases actualTypeArguments will contain Classes, since Class implements Type
}
if your case are a bit more complex (? extends String)` take a look at org.ormunit.entity.AEntityAccessor#extractClass
If you have specific field you can just check it like below:
private <T> String someMethod(T genericElement)
{
if (String.class.isInstance(genericElement))
{
return (String) genericElement;
}
...
What is the best way to retrieve the runtime value of a generic parameter for a generic class? For example:
public class MyClass<T> {
public void printT() {
// print the class of T, something like:
// System.out.println(T.class.getName());
}
}
So if I call
new MyClass<String>().printT()
it will print "String"
You don't. Due to type erasure that information is (mostly) lost at runtime. If you really need the class this is what you do:
public class MyClass<T> {
private final Class<T> clazz;
public MyClass(Class<T> c) {
if (c == null) {
throw new NullPointerException("class cannot be null");
}
clazz = c;
}
public void printT() {
System.out.println(clazz.getName());
}
}
and then you have access to it.
To achieve that, you need to add the type info, since type erasure means that T's type is not available.
public class MyClass<T> {
private final Class<T> clazz;
public MyClass(Class<T> clazz) {
this.clazz=clazz;
}
public void printT() {
// print the class of T, something like:
System.out.println(this.clazz);
}
}
Java doesn't have that information at runtime, because of type erasure. You would need to pass the type as a constructor parameter to the class when the object is instantiated. There are some libraries and languages which can help you to do that with less typing: Guice can do it and also Scala can do it.
As mentioned before, you can't obtain type information at that example because of type erasure.
However, you can redesign your classes hierarchy in order to have a generic superclass/interface and make child classes directly define type parameter at their definitions:
package com;
import java.lang.reflect.ParameterizedType;
import java.util.Arrays;
public class AAA {
public static void main(String[] args) throws Exception {
Object target = new MyClass<Integer>() {}; // child class that explicitly defines superclass type parameter is declared here
ParameterizedType type = (ParameterizedType) target.getClass().getGenericSuperclass();
System.out.println(Arrays.toString(type.getActualTypeArguments()));
}
}
class MyClass<T> {
}