Java Generic Class - Determine Type

Java Generic Class - Determine Type

I've used a similar solution to what he explains here for a few projects and found it pretty useful.

http://blog.xebia.com/2009/02/07/acessing-generic-types-at-runtime-in-java/

The jist of it is using the following:

 public Class returnedClass() {
ParameterizedType parameterizedType = (ParameterizedType)getClass()
.getGenericSuperclass();
return (Class) parameterizedType.getActualTypeArguments()[0];
}

How to check if a generic type implements a specific type of generic interface in java?

Java implements erasure, so there's no way to tell on runtime if genericObject is an instance of Set<String> or not. The only way to guarantee this is to use bounds on your generics, or check all elements in the set.

Compile-time Generic Bounds

Using bounds checking, which will be checked at compile-time:

public <T extends SomeInterface> void genericMethod(Set<? extends T> tSet) {
// Do something with tSet here
}

Java 8

We can use streams in Java 8 to do this natively in a single line:

public <T> void genericMethod(T t) {
if (t instanceof Set<?>) {
Set<?> set = (Set<?>) t;
if (set.stream().allMatch(String.class:isInstance)) {
Set<String> strs = (Set<String>) set;
// Do something with strs here
}
}
}

Java 7 and older

With Java 7 and older, we need to use iteration and type checking:

public <T> void genericMethod(T t) {
Set<String> strs = new HashSet<String>();
Set<?> tAsSet;
if (t instanceof Set<?>) {
tAsSet = (Set<?>) t;
for (Object obj : tAsSet) {
if (obj instanceof String) {
strs.add((String) obj);
}
}
// Do something with strs here
} else {
// Throw an exception or log a warning or something.
}
}

Guava

As per Mark Peters' comment below, Guava also has methods that do this for you if you can add it to your project:

public <T> void genericMethod(T t) {
if (t instanceof Set<?>) {
Set<?> set = (Set<?>) t;
if (Iterables.all(set, Predicates.instanceOf(String.class))) {
Set<String> strs = (Set<String>) set;
// Do something with strs here
}
}
}

The statement, Iterables.all(set, Predicates.instanceOf(String.class)) is essentially the same thing as set instanceof Set<String>.

How do I get a class instance of generic type T?

The short answer is, that there is no way to find out the runtime type of generic type parameters in Java. I suggest reading the chapter about type erasure in the Java Tutorial for more details.

A popular solution to this is to pass the Class of the type parameter into the constructor of the generic type, e.g.

class Foo<T> {
final Class<T> typeParameterClass;

public Foo(Class<T> typeParameterClass) {
this.typeParameterClass = typeParameterClass;
}

public void bar() {
// you can access the typeParameterClass here and do whatever you like
}
}

How to determine the class of a generic type?

Still the same problems : Generic informations are erased at runtime, it cannot be recovered. A workaround is to pass the class T in parameter of a static method :

public class MyGenericClass<T> {

private final Class<T> clazz;

public static <U> MyGenericClass<U> createMyGeneric(Class<U> clazz) {
return new MyGenericClass<U>(clazz);
}

protected MyGenericClass(Class<T> clazz) {
this.clazz = clazz;
}

public void doSomething() {
T instance = clazz.newInstance();
}
}

It's ugly, but it works.

Check if a class is generic type

From the Class<?> object you can check cls.getTypeParameters().

From a generic class you get a nonempty array of TypeVariable.

From a non-generic class you get an empty array.

E.g.

java.util.List.class.getTypeParameters()
TypeVariable[1] { E }
String.class.getTypeParameters()
TypeVariable[0] {}

How to determine type in a generic method in Java?

Due to type erasure, the only way to handle the case where the default value can be null is to have the method require an additional parmeter of type Class - which is generally better because it allows the default value to be a subclass of the required type.

How can I determine the type of a generic field in Java?

Have a look at Obtaining Field Types from the Java Tutorial Trail: The Reflection API.

Basically, what you need to do is to get all java.lang.reflect.Field of your class and call Field#getType() on each of them (check edit below). To get all object fields including public, protected, package and private access fields, simply use Class.getDeclaredFields(). Something like this:

for (Field field : Person.class.getDeclaredFields()) {
System.out.format("Type: %s%n", field.getType());
System.out.format("GenericType: %s%n", field.getGenericType());
}

EDIT: As pointed out by wowest in a comment, you actually need to call Field#getGenericType(), check if the returned Type is a ParameterizedType and then grab the parameters accordingly. Use ParameterizedType#getRawType() and ParameterizedType#getActualTypeArgument() to get the raw type and an array of the types argument of a ParameterizedType respectively. The following code demonstrates this:

for (Field field : Person.class.getDeclaredFields()) {
System.out.print("Field: " + field.getName() + " - ");
Type type = field.getGenericType();
if (type instanceof ParameterizedType) {
ParameterizedType pType = (ParameterizedType)type;
System.out.print("Raw type: " + pType.getRawType() + " - ");
System.out.println("Type args: " + pType.getActualTypeArguments()[0]);
} else {
System.out.println("Type: " + field.getType());
}
}

And would output:

Field: name - Type: class java.lang.String
Field: children - Raw type: interface java.util.List - Type args: class foo.Person

Get generic type of java.util.List

If those are actually fields of a certain class, then you can get them with a little help of reflection:

package test;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;

public class Test {

List<String> stringList = new ArrayList<String>();
List<Integer> integerList = new ArrayList<Integer>();

public static void main(String... args) throws Exception {
Field stringListField = Test.class.getDeclaredField("stringList");
ParameterizedType stringListType = (ParameterizedType) stringListField.getGenericType();
Class<?> stringListClass = (Class<?>) stringListType.getActualTypeArguments()[0];
System.out.println(stringListClass); // class java.lang.String.

Field integerListField = Test.class.getDeclaredField("integerList");
ParameterizedType integerListType = (ParameterizedType) integerListField.getGenericType();
Class<?> integerListClass = (Class<?>) integerListType.getActualTypeArguments()[0];
System.out.println(integerListClass); // class java.lang.Integer.
}
}

You can also do that for parameter types and return type of methods.

But if they're inside the same scope of the class/method where you need to know about them, then there's no point of knowing them, because you already have declared them yourself.

How to check generic type in generic method

You need an additional parameter of type Class<T>, because after type erasure the generic information will be erased. However, having a Class<T> will allow you to access (meta) information about T at Runtime:

protected <T> MyClass<T> myMethod(MyClass<T> myObj, Class<T> clazz) {
if (clazz == String.class) { ... }
}


Related Topics



Leave a reply



Submit