Java get class type variable

Get «real» class of generic type

You can get this through reflection. BUT. bear in mind if you have to treat your data differently depending on its generic type, you’re doing it wrong. You shouldn’t have to. If you do have to, it’s not as generic as you think it is, and that means there’s a problem in your class.

@glowcoder: One example where this might be valid, which I’ve run into myself, is accessing a static property of the generic type. One workaround is defining an instance that you never really use, like T obj; , so you can say obj.static_property later on. Also, you can do ((T)null).static_property .

If you have an object of that type, you could call the getClass method on that object. Not sure if that helps.

5 Answers 5

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 var; public static void main(String[] args) < Testa = new Test(); 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; >> 

Consider the case where var is set to an instance of class X where X extends T. boo() will print X instead of T.

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.

well, not specifically true, it’s in the class file as class or method attributes. It’s just not loaded into memory at runtime.

As others have explained, you cannot do it in that fashion but this is how it’s usually achieved in java.

public class MyClass  < public void method(Classclazz) < // something System.out.println(clazz.getName()); // something >> 
new MyClass().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.

Читайте также:  Text shadow css properties

Consider the following example «Pair» class, which allows two object references to be returned through a POJO:

public class Pair  < public final U first; public final V second; public static Pair of (U first, V second) < return new Pair(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.

Источник

How to get the class of type variable in Java Generics

Is it possible to determinate the Class of the type argument in method doSomething() without having an explicit type variable/field or any constructor in ContainerTest Class? Update: Changed format of ContainerTest Class

6 Answers 6

The only way is to store the class in an instance variable and require it as an argument of the constructor:

public class ContainerTest  < private ClasstClass; public ContainerTest(Class tClass) < this.tCLass = tClass; >public void doSomething() < //access tClass here >> 

If you are interested in the reflection way, I found a partial solution in this great article: http://www.artima.com/weblogs/viewpost.jsp?thread=208860

In short, you can use java.lang.Class.getGenericSuperclass() and java.lang.reflect.ParameterizedType.getActualTypeArguments() methods, but you have to subclass some parent super class.

Following snippet works for a class that directly extends the superclass AbstractUserType . See the referenced article for more general solution.

import java.lang.reflect.ParameterizedType; public class AbstractUserType  < public ClassreturnedClass() < ParameterizedType parameterizedType = (ParameterizedType) getClass() .getGenericSuperclass(); @SuppressWarnings("unchecked") Classret = (Class) parameterizedType.getActualTypeArguments()[0]; return ret; > public static void main(String[] args) < AbstractUserTypemyVar = new AbstractUserType() <>; System.err.println(myVar.returnedClass()); > > 

Источник

Узнаем параметр Generic-класса в Java

Недавно понадобилось решить следующую задачу: определить класс, которым параметризован generic-класс.

Если кто-то сталкивался с подобной задачей, то наверное также сразу попробовал написать что-то вроде этого:

Увы, IDE либо компилятор сразу укажут вам на ошибку («cannot select from a type variable» в стандартном компиляторе): » E. class » — не является допустимой конструкцией. Дело в том, что в общем случае во время исполнения программы информации о реальных параметрах нашего generic-класса может уже и не быть. Поэтому такая конструкция в Java не может работать.

то из-за стирания типов мы не можем анализируя listOfNumbers узнать, что это — ArrayList параметризованный именно Float, а не чем-то еще. К сожалению Java Generics работают именно так 🙁

Неужели информации о параметрах generic-классов при компиляции всегда теряется бесследно и не существует во время выполнения? Нет, существует. Но только в информации о классе, который явно определяет значение параметра в его generic-родителе. Выделим исследуемый класс:

Теперь, если мы будем анализировать через отражения listOfNumbers, мы сможем узнать, что это объект класса FloatList, для которого предком является ArrayList и этот ArrayList внутри FloatList был параметризован классом Float. Узнать всё это нам поможет метод Class.getGenericSuperclass().

Class actualClass = listOfNumbers.getClass();
ParameterizedType type = (ParameterizedType)actualClass.getGenericSuperclass();
System. out .println(type); // java.util.ArrayList
Class parameter = (Class)type.getActualTypeArguments()[0];
System. out .println(parameter); // class java.lang.Float

Таким образом, теперь мы можем узнать актуальный параметр generic-класса, если этот параметр был задан явным образом (то есть параметр определен внутри секции extends одного из наследников). Пусть мы не можем решить проблему определения типа параметра в общем виде, но во многих случаях даже того, что мы получили — достаточно.

Читайте также:  Java tcp connection class

Вынесем всё в отдельный метод:

public class ReflectionUtils public static Class getGenericParameterClass(Class actualClass, int parameterIndex) return (Class) ((ParameterizedType) actualClass.getGenericSuperclass()).getActualTypeArguments()[parameterIndex];
>
>

public class AbstractEntityFactory public Class getEntityClass() return ReflectionUtils.getGenericParameterClass( this .getClass(), 0);
>
>

Всё, проблема решена! Или нет.

Предположим, что от FloatList будет унаследован класс ExtendedFloatList? Очевидно, что actualClass.getGenericSuperclass() вернет нам уже не тот класс, который надо (FloatList вместо ExtendedFloatList). А если иерархия будет еще сложнее? Наш метод оказывается никуда не годным. Обобщим нашу задачу. Пркдставим, что у нас есть такая иерархия классов:

public class ReflectionUtilsTest extends TestCase // В комментариях приведены «реальные» параметры

static class A // String, Integer
>

static class B extends A // Integer, String, Set
>

Пусть теперь нам нужно из экземпляра класса E достать информацию о том, что его предок B в качестве второго параметра (Q) получил класс String.

Итак, что изменилось? Во-первых, теперь нам нужно анализировать не непосредственного родителя, а «подняться» по иерархии классов до определенного предка. Во-вторых, нам нужно учитывать, что параметры могут быть заданы не в ближайшем наследнике анализируемого класса, а «ниже». В-третьих, простой каст параметра к Class может не пройти — сам параметр может быть параметризованным классом. Попробуем всё это учесть…

import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Stack;

/**
* Alex Tracer (c) 2009
*/
public class ReflectionUtils

/**
* Для некоторого класса определяет каким классом был параметризован один из его предков с generic-параметрами.
*
* @param actualClass анализируемый класс
* @param genericClass класс, для которого определяется значение параметра
* @param parameterIndex номер параметра
* @return класс, являющийся параметром с индексом parameterIndex в genericClass
*/
public static Class getGenericParameterClass(final Class actualClass, final Class genericClass, final int parameterIndex) // Прекращаем работу если genericClass не является предком actualClass.
if (!genericClass.isAssignableFrom(actualClass.getSuperclass())) throw new IllegalArgumentException( «Class » + genericClass.getName() + » is not a superclass of »
+ actualClass.getName() + «.» );
>

// Нам нужно найти класс, для которого непосредственным родителем будет genericClass.
// Мы будем подниматься вверх по иерархии, пока не найдем интересующий нас класс.
// В процессе поднятия мы будем сохранять в genericClasses все классы — они нам понадобятся при спуске вниз.

// Проейденные классы — используются для спуска вниз.
Stack genericClasses = new Stack();

// clazz — текущий рассматриваемый класс
Class clazz = actualClass;

Читайте также:  Decimal float разница python

while ( true ) Type genericSuperclass = clazz.getGenericSuperclass();
boolean isParameterizedType = genericSuperclass instanceof ParameterizedType;
if (isParameterizedType) // Если предок — параметризованный класс, то запоминаем его — возможно он пригодится при спуске вниз.
genericClasses.push((ParameterizedType) genericSuperclass);
> else // В иерархии встретился непараметризованный класс. Все ранее сохраненные параметризованные классы будут бесполезны.
genericClasses.clear();
>
// Проверяем, дошли мы до нужного предка или нет.
Type rawType = isParameterizedType ? ((ParameterizedType) genericSuperclass).getRawType() : genericSuperclass;
if (!rawType.equals(genericClass)) // genericClass не является непосредственным родителем для текущего класса.
// Поднимаемся по иерархии дальше.
clazz = clazz.getSuperclass();
> else // Мы поднялись до нужного класса. Останавливаемся.
break ;
>
>

// Нужный класс найден. Теперь мы можем узнать, какими типами он параметризован.
Type result = genericClasses.pop().getActualTypeArguments()[parameterIndex];

while (result instanceof TypeVariable && !genericClasses.empty()) // Похоже наш параметр задан где-то ниже по иерархии, спускаемся вниз.

// Получаем индекс параметра в том классе, в котором он задан.
int actualArgumentIndex = getParameterTypeDeclarationIndex((TypeVariable) result);
// Берем соответствующий класс, содержащий метаинформацию о нашем параметре.
ParameterizedType type = genericClasses.pop();
// Получаем информацию о значении параметра.
result = type.getActualTypeArguments()[actualArgumentIndex];
>

if (result instanceof TypeVariable) // Мы спустились до самого низа, но даже там нужный параметр не имеет явного задания.
// Следовательно из-за «Type erasure» узнать класс для параметра невозможно.
throw new IllegalStateException( «Unable to resolve type variable » + result + «.»
+ » Try to replace instances of parametrized class with its non-parameterized subtype.» );
>

if (result instanceof ParameterizedType) // Сам параметр оказался параметризованным.
// Отбросим информацию о его параметрах, она нам не нужна.
result = ((ParameterizedType) result).getRawType();
>

if (result == null ) // Should never happen. 🙂
throw new IllegalStateException( «Unable to determine actual parameter type for »
+ actualClass.getName() + «.» );
>

if (!(result instanceof Class)) // Похоже, что параметр — массив или что-то еще, что не является классом.
throw new IllegalStateException( «Actual parameter type for » + actualClass.getName() + » is not a Class.» );
>

public static int getParameterTypeDeclarationIndex(final TypeVariable typeVariable) GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();

// Ищем наш параметр среди всех параметров того класса, где определен нужный нам параметр.
TypeVariable[] typeVariables = genericDeclaration.getTypeParameters();
Integer actualArgumentIndex = null ;
for ( int i = 0; i < typeVariables.length; i++) if (typeVariables[i].equals(typeVariable)) actualArgumentIndex = i;
break ;
>
>
if (actualArgumentIndex != null ) return actualArgumentIndex;
> else throw new IllegalStateException( «Argument » + typeVariable.toString() + » is not found in »
+ genericDeclaration.toString() + «.» );
>
>
>

Ухх, наш метод «в одну строчку» превратился в громоздкого монстра! 🙂
Надеюсь комментариев достаточно, чтобы понять происходящее 😉

Итак, перепишем наш начальный класс:

public class AbstractEntityFactory public Class getEntityClass() return ReflectionUtils.getGenericParameterClass( this .getClass(), AbstractEntityFactory. class , 0);
>
>

public class TopicFactory extends AbstractEntityFactory public void doSomething() Class entityClass = getEntityClass(); // Вернет Topic
>
>

На этом пожалуй всё. Спасибо что дочитали до конца 🙂

Это мой первый пост на Хабре. Буду благодарен за критику, замечания и указания на ошибки.

Upd: код исправлен для корректного учета ситуации, когда где-то в иерархии присутствует непараметризованный класс.
Upd2: спасибо пользователю Power за указание на ошибки.

Источник

Оцените статью