Я просто предполагаю, что любой, кто читает этот ответ, уже знает о стирании типа. Если нет, следуйте Введите Erasure< /em>, и я бы также рекомендовал прочитать вопросы и ответы стирание типа дженериков Java: когда и что бывает.
Объяснение стирания типа дает некоторую полезную информацию, но на самом деле оно не объясняет, что делает getActualTypeArguments()
и почему код в вопросе не работает.
Для Class c
c.getGenericSuperclass()
эффективно получает тип в предложении extends c
.
Так, например, если бы у нас было это (с использованием реального класса):
public class ArrayList<E> extends AbstractList<E> {...}
И мы сказали ArrayList.class.getGenericSuperclass()
, мы получили параметризованный тип, представляющий AbstractList<E>
. Опять же, это тип в предложении extends.
Если бы у нас было это:
ArrayList<Long> al = new ArrayList<Long>();
al.getClass()
возвращает ArrayList.class
, поэтому al.getClass().getGenericSuperclass()
снова возвращает AbstractList<E>
.
«Фактическим» аргументом типа для AbstractList<E>
является переменная типа E
, поэтому, если бы мы сказали:
Type actualTypeArgument =
((ParameterizedType)
al.getClass()
.getGenericSuperclass())
.getActualTypeArguments() [0];
Тогда actualTypeArgument
ссылается на переменную типа E
, объявленную ArrayList
. (И на самом деле это так, что actualTypeArgument == ArrayList.class.getTypeParameters()[0]
верно.)
Что обычно делается при использовании этой идиомы, так это создание анонимного подкласса каждый раз, когда вы создаете экземпляр типа, аргумент типа которого вы хотите захватить:
ArrayList<Long> al = new ArrayList<Long>() {};
// Note ^^
Это неявно объявляет такой класс:
class AnonymousArrayList1 extends ArrayList<Long> {}
(За исключением того, что у анонимного класса нет имени. Кроме того, мы могли бы объявить подобный подкласс явно, вместо того, чтобы использовать анонимный класс.)
Теперь, когда мы вызываем al.getClass()
, мы получаем AnonymousArrayList1.class
, чей общий суперкласс — ArrayList<Long>
. Теперь вы можете получить фактический аргумент типа ArrayList<Long>
, то есть Long
.
(Вот короткая программа, демонстрирующая все вышеперечисленное.)
Что касается разрешения уровня косвенности, то это возможно, но код более сложен. Вы также по-прежнему связаны приведенным выше правилом, согласно которому фактический класс должен быть предоставлен в качестве аргумента типа в предложении extends.
Вот пример этого, который позволяет любое количество промежуточных суперклассов:
package mcve;
import java.lang.reflect.*;
public abstract class KnowMyType<T> {
public KnowMyType() {
System.out.println(getMyType());
}
@SuppressWarnings("unchecked")
private Class<T> getMyType() {
try {
return (Class<T>) findMyType(getClass(), null);
} catch (RuntimeException x) {
throw new IllegalArgumentException("illegal type argument", x);
}
}
private Type findMyType(Class<?> plainClass, Type genericClass) {
Class<?> plainSuper = plainClass.getSuperclass();
Type genericSuper = plainClass.getGenericSuperclass();
Type t;
if (plainSuper == KnowMyType.class) {
t = ((ParameterizedType) genericSuper).getActualTypeArguments()[0];
} else {
t = findMyType(plainSuper, genericSuper);
}
if (t instanceof TypeVariable<?>) {
TypeVariable<?>[] vars = plainClass.getTypeParameters();
for (int i = 0; i < vars.length; ++i) {
if (t == vars[i]) {
t = ((ParameterizedType) genericClass).getActualTypeArguments()[i];
break;
}
}
}
return t;
}
}
Это позволит вам сделать это:
class KnowMyTypeSub<T> extends KnowMyType<T> {}
new KnowMyTypeSub<Long>() {}; // constructor prints 'class java.lang.Long'
Гуава TypeToken
a> также делает подобные вещи автоматически.
person
Radiodef
schedule
17.07.2018
Class<T>
вашемуGenericClass
из подкласса - person Lino   schedule 17.07.2018Class<T>
, вероятно, лучший способ, поскольку он не зависит от медленного отражения. - person Lino   schedule 17.07.2018