Как определить класс родового типа?
Я создаю общий класс и в одном из методов мне нужно знать класс используемого в настоящее время типа общего типа. Причина в том, что один из методов, которые я вызываю, ожидает в качестве аргумента.
Пример:
public class MyGenericClass<T> {
public void doSomething() {
// Snip...
// Call to a 3rd party lib
T bean = (T)someObject.create(T.class);
// Snip...
}
}
Очевидно, что приведенный выше пример не работает и приводит к следующей ошибке: Недопустимый литерал класса для параметра типа T.
Мой вопрос: кто-то знает хорошую альтернативу или обходное решение для этого?
Ответы
Ответ 1
Все те же проблемы: общая информация удаляется во время выполнения, ее невозможно восстановить. Обходным путем является передача класса T в параметр статического метода:
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();
}
}
Это уродливо, но оно работает.
Ответ 2
Я просто указал на это решение:
import java.lang.reflect.ParameterizedType;
public abstract class A<B> {
public Class<B> g() throws Exception {
ParameterizedType superclass =
(ParameterizedType) getClass().getGenericSuperclass();
return (Class<B>) superclass.getActualTypeArguments()[0];
}
}
Это работает, если A
задан конкретным типом подкласса:
new A<String>() {}.g() // this will work
class B extends A<String> {}
new B().g() // this will work
class C<T> extends A<T> {}
new C<String>().g() // this will NOT work
Ответ 3
К сожалению, решение Кристофа, как написано, работает только в очень ограниченных обстоятельствах. [EDIT: как было сказано ниже, я больше не помню свои рассуждения для этого предложения, и это, скорее всего, неверно: "Обратите внимание, что это будет работать только в абстрактных классах, прежде всего".] Следующая трудность заключается в том, что g()
работает только с DIRECT подклассы A
. Мы можем это исправить, хотя:
private Class<?> extractClassFromType(Type t) throws ClassCastException {
if (t instanceof Class<?>) {
return (Class<?>)t;
}
return (Class<?>)((ParameterizedType)t).getRawType();
}
public Class<B> g() throws ClassCastException {
Class<?> superClass = getClass(); // initial value
Type superType;
do {
superType = superClass.getGenericSuperclass();
superClass = extractClassFromType(superType);
} while (! (superClass.equals(A.class)));
Type actualArg = ((ParameterizedType)superType).getActualTypeArguments()[0];
return (Class<B>)extractClassFromType(actualArg);
}
Это будет работать во многих ситуациях на практике, но не ВСЕ. Рассмотрим:
public class Foo<U,T extends Collection<?>> extends A<T> {}
(new Foo<String,List<Object>>() {}).g();
Это вызовет ClassCastException
, потому что аргумент типа здесь вообще не является Class
или ParameterizedType
; это TypeVariable
T
. Итак, теперь вы застряли, пытаясь понять, какой тип T
должен был стоять, и так далее вниз по кроличьей дыре.
Я думаю, что единственный разумный общий ответ - это нечто похожее на исходный ответ Николаса - в общем, если вашему классу необходимо создать объекты какого-либо другого класса, который неизвестен во время компиляции, пользователям вашего класса необходимо передать это класса (или, возможно, a Factory) в ваш класс явно и не полагаться исключительно на дженерики.
Ответ 4
Я найду другой способ получить класс общего объекта
public Class<?> getGenericClass(){
Class<?> result =null;
Type type =this.getClass().getGenericSuperclass();
if(type instanceofParameterizedType){
ParameterizedType pt =(ParameterizedType) type;
Type[] fieldArgTypes = pt.getActualTypeArguments();
result =(Class<?>) fieldArgTypes[0];
}
return result;
}
Ответ 5
T можно легко решить с помощью TypeTools:
Class<T> t = (Class<T>) TypeResolver.resolveRawArguments(
MyGenericClass.class, getClass());
Ответ 6
Я расскажу о решении Кристофа.
Вот абстрактный класс ClassGetter:
private abstract class ClassGetter<T> {
public final Class<T> get() {
final ParameterizedType superclass = (ParameterizedType)
getClass().getGenericSuperclass();
return (Class<T>)superclass.getActualTypeArguments()[0];
}
}
Вот статический метод, который использует вышеприведенный класс для поиска типа универсального класса:
public static <T> Class<T> getGenericClass() {
return new ClassGetter<T>() {}.get();
}
В качестве примера использования этого метода вы можете сделать этот метод:
public static final <T> T instantiate() {
final Class<T> clazz = getGenericClass();
try {
return clazz.getConstructor((Class[])null).newInstance(null);
} catch (Exception e) {
return null;
}
}
И затем используйте его следующим образом:
T var = instantiate();