Получить универсальный тип класса во время выполнения

508

Как мне этого добиться?

public class GenericClass<T>
{
    public Type getMyType()
    {
        //How do I return the type of T?
    }
}

Все, что я пробовал до сих пор, всегда возвращает тип, Objectа не конкретный используемый тип.

Гленн
источник
3
@SamuelVidal Это не .NET, это Java.
Frontear

Ответы:

317

Как уже упоминалось, это возможно только путем отражения в определенных обстоятельствах.

Если вам действительно нужен тип, это обычный (безопасный для типов) шаблон обхода:

public class GenericClass<T> {

     private final Class<T> type;

     public GenericClass(Class<T> type) {
          this.type = type;
     }

     public Class<T> getMyType() {
         return this.type;
     }
}
Henning
источник
58
Мне нравится этот ответ, но создавать его немного громоздко: GenericClass <AnotherClass> g = new GenericClass <AnotherClass> (AnotherClass.class);
Eliseo Ocampos
1
Это еще более многословно, если вы используете подход dao / factory / manager. Foo foo1 = GetDao<Foo>(Foo.class).get(Foo.class, 1)
djmj
2
Это правда, но работает не во всех случаях, как удаленные компоненты без состояния, которые создаются контейнером / отражением.
djmj
6
Так же, как продолжение моего предыдущего комментария - после большой боли, играя с размышлениями, я в конечном итоге использовал этот ответ.
Томаш Зато - Восстановить Монику
18
Вы можете обойти лишнюю ссылку, предоставив общий статический метод фабрики. Что-то вроде, public static <T> GenericClass<T> of(Class<T> type) {...}а затем назвать это так GenericClass<String> var = GenericClass.of(String.class). Немного приятнее.
Джоэри Хендрикс
263

Я видел что-то подобное

private Class<T> persistentClass;

public Constructor() {
    this.persistentClass = (Class<T>) ((ParameterizedType) getClass()
                            .getGenericSuperclass()).getActualTypeArguments()[0];
 }

в примере hibernate GenericDataAccessObjects

FrVaBe
источник
84
Этот метод работает там, где параметр типа определен в непосредственном суперклассе, но не работает, если параметр типа определен в другом месте в иерархии типов. Для обработки более сложных случаев можно использовать что-то вроде TypeTools . Документы содержат пример более сложного общего DAO.
Джонатан
25
Он возвращает только фактические параметры типа, используемые, когда CLASS реализует / расширяет что-то, имеющее общие объявления, он не возвращает фактические параметры типа, используемые при создании экземпляра INSTANCE . Другими словами, он МОЖЕТ сказать class A implements Comparable<String>, что фактическим параметром типа является String, но он НЕ МОЖЕТ сказать Set<String> a = new TreeSet<String>(), что фактическим параметром типа является String. Фактически, информация о параметре типа «стирается» после компиляции, как объяснено в других ответах.
Сиу Чинг Понг -Асука Кэндзи -
32
Я получаю java.lang.Class cannot be cast to java.lang.reflect.ParameterizedTypeза этот ответ.
Томаш Зато - Восстановить Монику
4
Этот подход также может быть достигнут с использованием Class-Mateот людей Джексона. Я написал здесь суть gist.github.com/yunspace/930d4d40a787a1f6a7d1
yunspace
5
@ TomášZato Вызов простого кода выше вернул мне то же исключение. Я знаю, что уже немного поздно, но в любом случае в моем случае мне пришлось позвонить, (Class<T>) ((ParameterizedType)getClass().getSuperclass().getGenericSuperclass()).getActualTypeArguments()чтобы получить фактические аргументы типа.
AndrewMcCoist
99

Обобщения не овеществлены во время выполнения. Это означает, что информация отсутствует во время выполнения.

Добавление дженериков в Java с одновременной поддержкой обратной совместимости было непростой задачей (вы можете увидеть оригинальный документ об этом: сделать будущее безопасным для прошлого: добавить дженерики в язык программирования Java ).

По этому вопросу имеется обширная литература, и некоторые люди недовольны текущим состоянием, некоторые говорят, что на самом деле это приманка и в этом нет особой необходимости. Вы можете прочитать обе ссылки, я нашел их довольно интересными.

ewernli
источник
179
Конечно, мы недовольны, .NET имеет гораздо лучший общий механизм обработки
Pacerier
6
@Pacerier: но только усовершенствованные дженерики не приведут Java к уровню .NET. Типы значений, специализированный код для которых, по крайней мере, в равной степени важен, потому что .NET лучше в области обобщений.
Иоахим Зауэр
@JoachimSauer, да, типы значений. Я всегда хотел, чтобы те в Java. Кстати, что вы подразумеваете под специализированным кодом?
Pacerier
3
@ spaaarky21 Нет, параметры универсального типа удаляются во время компиляции (так называемое «стирание», вы можете погуглить его). Уловка в ответе FrVaBe работает, только если типовые параметры суперкласса известны статически (см. Первый комментарий Джонатна)
ewernli
1
Стирание типа Java является историческим недостатком дизайна; больше кода было написано, чтобы обойти это, чем было написано для его реализации.
Абхиджит Саркар
68

Используйте гуаву.

import com.google.common.reflect.TypeToken;
import java.lang.reflect.Type;

public abstract class GenericClass<T> {
  private final TypeToken<T> typeToken = new TypeToken<T>(getClass()) { };
  private final Type type = typeToken.getType(); // or getRawType() to return Class<? super T>

  public Type getType() {
    return type;
  }

  public static void main(String[] args) {
    GenericClass<String> example = new GenericClass<String>() { };
    System.out.println(example.getType()); // => class java.lang.String
  }
}

Некоторое время назад, я отправил несколько примеров полноценных включая абстрактные классы и подклассы здесь .

Примечание: это требует , чтобы вы создать экземпляр подкласса из GenericClassтак что он может правильно связать параметр типа. В противном случае он просто вернет тип как T.

Коди А. Рэй
источник
3
Обратите внимание, что я создаю пустой анонимный подкласс (см. Две фигурные скобки в конце). Это использует отражение для борьбы со стиранием типов во время выполнения Java. Вы можете узнать больше здесь: code.google.com/p/guava-libraries/wiki/ReflectionExplained
Коди А. Рэй,
3
@ CodyA.Ray Ваш код выбрасывает java.lang.IllegalArgumentException: class com.google.common.reflect.TypeToken isn't parameterized. Поэтому я изменил строку new TypeToken(getClass()) { }на new TypeToken<T>(getClass()) { }. Теперь код работает нормально, но Type по-прежнему 'T'. Смотрите это: gist.github.com/m-manu/9cda9d8f9d53bead2035
Ману Манджунатх
3
@Dominik Пожалуйста, посмотрите обновленный пример, который вы можете скопировать и вставить, чтобы проверить себя. Я также добавил примечание, поясняющее, что вы должны создать экземпляр подкласса (как показано). Как совет общего этикета, пожалуйста, прочитайте любые связанные статьи и связанные с ним javadocs, прежде чем обвинить плакат в «желаемом за действии». Я использовал похожий производственный код несколько раз. Помощники Гуавы, которые я демонстрирую, предназначены именно для этого случая использования, и их javadocs показывают почти точный ответ на этот вопрос. docs.guava-libraries.googlecode.com/git/javadoc/com/google/…
Коди А. Рэй
1
Этот ответ прекрасно работает с Java8 - у меня есть интерфейс и фабричный класс для передачи различных типов, и я хотел, чтобы пользователи могли легко определять тип. В моем интерфейсе я добавил метод по умолчанию: default Type getParameterType() { final TypeToken<T> typeToken = new TypeToken<T>(getClass()) {}; final Type type = typeToken.getType(); return type; }
Ричард Санд
2
@ CodyA.Ray Так как это работает только с подклассами GenericClass, вы должны сделать этот класс, abstractчтобы неправильное использование не компилировалось.
Мартин
37

Что вы можете.

Java не использует информацию во время выполнения по причинам обратной совместимости. Но информация фактически представлена ​​в виде метаданных и может быть доступна через отражение (но она все еще не используется для проверки типов).

Из официального API:

http://download.oracle.com/javase/6/docs/api/java/lang/reflect/ParameterizedType.html#getActualTypeArguments%28%29

Однако для вашего сценария я бы не использовал рефлексию. Я лично более склонен использовать это для кода платформы. В вашем случае я бы просто добавил тип в качестве параметра конструктора.

Джори Хендрикс
источник
10
getActualTypeArguments возвращает только аргументы типа для непосредственного класса. Если у вас есть иерархия сложного типа, где T может быть параметризован где-либо в иерархии, вам нужно будет немного поработать, чтобы выяснить, что это такое. Это более или менее то, что делает TypeTools .
Джонатан
36

Обобщение Java в основном выполняется во время компиляции, это означает, что информация о типе теряется во время выполнения.

class GenericCls<T>
{
    T t;
}

будет скомпилировано в нечто вроде

class GenericCls
{
   Object o;
}

Чтобы получить информацию о типе во время выполнения, вы должны добавить ее в качестве аргумента ctor.

class GenericCls<T>
{
     private Class<T> type;
     public GenericCls(Class<T> cls)
     {
        type= cls;
     }
     Class<T> getType(){return type;}
}

Пример:

GenericCls<?> instance = new GenericCls<String>(String.class);
assert instance.getType() == String.class;
josefx
источник
9
private final Class<T> type;
NaXa
Как я могу создать тип массива из него:Type t = //String[]
Pawel Cioch
@PawelCioch java.lang.reflect.Array.newInstance (elementtype, length); надеюсь, это поможет (javadoc можно найти здесь docs.oracle.com/javase/8/docs/api/java/lang/reflect/… )
josefx
@PawelCioch пропустил .getClass (), чтобы получить тип из созданного массива. Кажется, не существует прямого способа получить класс массива. В большинстве коллекций Java вместо этого используется Object [].
Josefx
23
public abstract class AbstractDao<T>
{
    private final Class<T> persistentClass;

    public AbstractDao()
    {
        this.persistentClass = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass())
                .getActualTypeArguments()[0];
    }
}
Али еганех
источник
3
Я одобряю этот ответ, потому что это решение, которое работает для задаваемого вопроса. Однако для тех, кто хочет перейти вверх в иерархии классов, как я, с более чем одним родовым классом, это не сработает. Потому что вы получите java.lang.object вместо реального класса.
KMC
3
Обратите внимание, что это решение работает ТОЛЬКО, если класс, который содержит универсальный тип, является РЕЗЮМЕ
BabaNew
Не работал с моим абстрактным классом в Java 11
JRA_TLL
@JRA_TLL вы, очевидно, сделали что-то не так. Я просто использовал его с Java 12 и работает как шарм.
Гуги
Если вы хотите перейти вверх в иерархии представления, вы можете привести genericSuperclass к классу <*> и получить genericSuperclass. Желательно в цикле.
fupduck
21

Я использовал следующий подход:

public class A<T> {

    protected Class<T> clazz;

    public A() {
        this.clazz = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }

    public Class<T> getClazz() {
        return clazz;
    }
}

public class B extends A<C> {
   /* ... */
    public void anything() {
       // here I may use getClazz();
    }
}
Moesio
источник
9
Я получил "Исключение в потоке" main "java.lang.ClassCastException: java.lang.Class не может быть приведен к java.lang.reflect.ParameterizedType" с этим примером кода
yuyang
16

Я не думаю, что вы можете, Java использует стирание типов при компиляции, чтобы ваш код был совместим с приложениями и библиотеками, которые были созданы предварительными.

Из документов Oracle:

Тип Erasure

Обобщения были введены в язык Java для обеспечения более строгих проверок типов во время компиляции и для поддержки общего программирования. Для реализации обобщений компилятор Java применяет стирание типа к:

Замените все параметры типа в универсальных типах их границами или Object, если параметры типа не ограничены. Таким образом, полученный байт-код содержит только обычные классы, интерфейсы и методы. Вставьте слепки типа при необходимости, чтобы сохранить безопасность типа. Генерация мостовых методов для сохранения полиморфизма в расширенных универсальных типах. Стирание типов гарантирует, что новые классы для параметризованных типов не создаются; следовательно, генерики не несут затрат времени выполнения.

http://docs.oracle.com/javase/tutorial/java/generics/erasure.html

Димитар
источник
Юп, это невозможно. Java будет нуждаться в улучшенных обобщениях для того, чтобы это работало.
Хеннинг
15

Техника, описанная в этой статье Яном Робертсоном, работает для меня.

Короче быстрый и грязный пример:

 public abstract class AbstractDAO<T extends EntityInterface, U extends QueryCriteria, V>
 {
    /**
     * Method returns class implementing EntityInterface which was used in class
     * extending AbstractDAO
     *
     * @return Class<T extends EntityInterface>
     */
    public Class<T> returnedClass()
    {
        return (Class<T>) getTypeArguments(AbstractDAO.class, getClass()).get(0);
    }

    /**
     * Get the underlying class for a type, or null if the type is a variable
     * type.
     *
     * @param type the type
     * @return the underlying class
     */
    public static Class<?> getClass(Type type)
    {
        if (type instanceof Class) {
            return (Class) type;
        } else if (type instanceof ParameterizedType) {
            return getClass(((ParameterizedType) type).getRawType());
        } else if (type instanceof GenericArrayType) {
            Type componentType = ((GenericArrayType) type).getGenericComponentType();
            Class<?> componentClass = getClass(componentType);
            if (componentClass != null) {
                return Array.newInstance(componentClass, 0).getClass();
            } else {
                return null;
            }
        } else {
            return null;
        }
    }

    /**
     * Get the actual type arguments a child class has used to extend a generic
     * base class.
     *
     * @param baseClass the base class
     * @param childClass the child class
     * @return a list of the raw classes for the actual type arguments.
     */
    public static <T> List<Class<?>> getTypeArguments(
            Class<T> baseClass, Class<? extends T> childClass)
    {
        Map<Type, Type> resolvedTypes = new HashMap<Type, Type>();
        Type type = childClass;
        // start walking up the inheritance hierarchy until we hit baseClass
        while (!getClass(type).equals(baseClass)) {
            if (type instanceof Class) {
                // there is no useful information for us in raw types, so just keep going.
                type = ((Class) type).getGenericSuperclass();
            } else {
                ParameterizedType parameterizedType = (ParameterizedType) type;
                Class<?> rawType = (Class) parameterizedType.getRawType();

                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                TypeVariable<?>[] typeParameters = rawType.getTypeParameters();
                for (int i = 0; i < actualTypeArguments.length; i++) {
                    resolvedTypes.put(typeParameters[i], actualTypeArguments[i]);
                }

                if (!rawType.equals(baseClass)) {
                    type = rawType.getGenericSuperclass();
                }
            }
        }

        // finally, for each actual type argument provided to baseClass, determine (if possible)
        // the raw class for that type argument.
        Type[] actualTypeArguments;
        if (type instanceof Class) {
            actualTypeArguments = ((Class) type).getTypeParameters();
        } else {
            actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
        }
        List<Class<?>> typeArgumentsAsClasses = new ArrayList<Class<?>>();
        // resolve types by chasing down type variables.
        for (Type baseType : actualTypeArguments) {
            while (resolvedTypes.containsKey(baseType)) {
                baseType = resolvedTypes.get(baseType);
            }
            typeArgumentsAsClasses.add(getClass(baseType));
        }
        return typeArgumentsAsClasses;
    }
  }
Ондрей Бозек
источник
3
В какой конкретной строке в этом коде содержатся текущие параметры типа во время выполнения, которые читаются?
Иван Матавуль
Вот? Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
Ондрей Бозек
9

Я думаю, что есть другое элегантное решение.

То, что вы хотите сделать, это (безопасно) «передать» тип параметра универсального типа из конкретного класса в суперкласс.

Если вы позволяете себе думать о типе класса как о «метаданных» в классе, это предлагает метод Java для кодирования метаданных во время выполнения: аннотации.

Сначала определите пользовательскую аннотацию вдоль этих линий:

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EntityAnnotation {
    Class entityClass();
}

Затем вы можете добавить аннотацию к вашему подклассу.

@EntityAnnotation(entityClass =  PassedGenericType.class)
public class Subclass<PassedGenericType> {...}

Затем вы можете использовать этот код, чтобы получить тип класса в вашем базовом классе:

import org.springframework.core.annotation.AnnotationUtils;
.
.
.

private Class getGenericParameterType() {
    final Class aClass = this.getClass();
    EntityAnnotation ne = 
         AnnotationUtils.findAnnotation(aClass, EntityAnnotation.class);

    return ne.entityClass();
}

Некоторые ограничения этого подхода:

  1. Вы указываете универсальный тип ( PassedGenericType) в ДВУХ местах, а не в не СУХОЙ.
  2. Это возможно, только если вы можете изменить конкретные подклассы.
DaBlick
источник
Да, он не СУХОЙ, однако он чище, чем предложенный выше подход к расширению. Мне нравится. спасибо
agodinhost
8

Это мое решение:

import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;

public class GenericClass<T extends String> {

  public static void main(String[] args) {
     for (TypeVariable typeParam : GenericClass.class.getTypeParameters()) {
      System.out.println(typeParam.getName());
      for (Type bound : typeParam.getBounds()) {
         System.out.println(bound);
      }
    }
  }
}
Матиас М
источник
10
Это не ответ на этот вопрос.
Адам Арольд
1
Мой код не является точным решением вопроса. Он возвращает параметры универсального типа класса, но не фактический тип T. Но это может быть полезно для других, которые сталкиваются с вопросом и ищут мое решение.
Матиас М,
1
getClass (). getGenericSuperclass () достигнет того же эффекта.
Горький
5

Вот рабочее решение !!!

@SuppressWarnings("unchecked")
    private Class<T> getGenericTypeClass() {
        try {
            String className = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0].getTypeName();
            Class<?> clazz = Class.forName(className);
            return (Class<T>) clazz;
        } catch (Exception e) {
            throw new IllegalStateException("Class is not parametrized with generic type!!! Please use extends <> ");
        }
    } 

ЗАМЕЧАНИЯ: Может использоваться только как суперкласс
1. Должен быть расширен с помощью typed class ( Child extends Generic<Integer>)
ИЛИ

2. Должен быть создан как анонимная реализация ( new Generic<Integer>() {};).

Ярослав Ковбас
источник
4

Ты не можешь Если вы добавите в класс переменную-член типа T (вам даже не нужно ее инициализировать), вы можете использовать ее для восстановления типа.

andrewmu
источник
2
Ой, хорошо. Вы действительно должны инициализировать его из конструктора.
andrewmu
4

Вот один из способов, который мне пришлось использовать один или два раза:

public abstract class GenericClass<T>{
    public abstract Class<T> getMyType();
}

Вместе с

public class SpecificClass extends GenericClass<String>{

    @Override
    public Class<String> getMyType(){
        return String.class;
    }
}
PJWeisberg
источник
4
Технически это работает, однако это не решает общий случай, и я думаю, что это то, что после оригинального плаката.
ggb667
Это не заслуживает того, чтобы быть отвергнутым, как есть - оригинальный плакат не был явным. Этот ответ предлагает шаблон проектирования, который работает и его легко реализовать при условии, что он подходит для того, чтобы сделать обобщенный класс абстрактным.
VirtualMichael,
4

Одно простое решение для этой кабины, как показано ниже

public class GenericDemo<T>{
    private T type;

    GenericDemo(T t)
    {
        this.type = t;
    }

    public String getType()
    {
        return this.type.getClass().getName();
    }

    public static void main(String[] args)
    {
        GenericDemo<Integer> obj = new  GenericDemo<Integer>(5);
        System.out.println("Type: "+ obj.getType());
    }
}
Paul92
источник
3

Чтобы завершить некоторые ответы здесь, мне нужно было получить ParametrizedType MyGenericClass, независимо от того, насколько высока иерархия, с помощью рекурсии:

private Class<T> getGenericTypeClass() {
        return (Class<T>) (getParametrizedType(getClass())).getActualTypeArguments()[0];
}

private static ParameterizedType getParametrizedType(Class clazz){
    if(clazz.getSuperclass().equals(MyGenericClass.class)){ // check that we are at the top of the hierarchy
        return (ParameterizedType) clazz.getGenericSuperclass();
    } else {
        return getParametrizedType(clazz.getSuperclass());
    }
}
Galex
источник
1

Вот мой трюк:

public class Main {

    public static void main(String[] args) throws Exception {

        System.out.println(Main.<String> getClazz());

    }

    static <T> Class getClazz(T... param) {

        return param.getClass().getComponentType();
    }

}
HRgiger
источник
1
Примечание: это не работает, когда Tпеременная типа. В случае, если Tэто переменная типа, varargs создает массив для удаления T. Смотрите, например, http://ideone.com/DIPNwd .
Radiodef
1
Это возвращает "Объект"
yahya
Может быть, вы пытаетесь ответить на другой вопрос 🤔
Хан
1

В случае, если вы используете переменную store с использованием универсального типа, вы можете легко решить эту проблему, добавив метод getClassType следующим образом:

public class Constant<T> {
  private T value;

  @SuppressWarnings("unchecked")
  public Class<T> getClassType () {
    return ((Class<T>) value.getClass());
  }
}

Позже я использую предоставленный объект класса, чтобы проверить, является ли он экземпляром данного класса, следующим образом:

Constant<?> constant = ...;
if (constant.getClassType().equals(Integer.class)) {
    Constant<Integer> integerConstant = (Constant<Integer>)constant;
    Integer value = integerConstant.getValue();
    // ...
}
Пабло Тринидад
источник
2
Это проблематично, к сожалению. Прежде всего, что если valueесть null? Во-вторых, что, если valueэто подкласс T? Constant<Number> c = new Constant<Number>(new Integer(0)); Class<Number> n = c.getClassType();возвращается, Integer.classкогда он должен вернуться Number.class. Вернее было бы вернуться Class<? extends T>. Integer.class это, Class<? extends Number> но не Class<Number>.
Radiodef
1

Вот мое решение

public class GenericClass<T>
{
    private Class<T> realType;

    public GenericClass() {
        findTypeArguments(getClass());
    }

    private void findTypeArguments(Type t) {
        if (t instanceof ParameterizedType) {
            Type[] typeArgs = ((ParameterizedType) t).getActualTypeArguments();
            realType = (Class<T>) typeArgs[0];
        } else {
            Class c = (Class) t;
            findTypeArguments(c.getGenericSuperclass());
        }
    }

    public Type getMyType()
    {
        // How do I return the type of T? (your question)
        return realType;
    }
}

Независимо от того, сколько уровней имеет ваша иерархия классов, это решение все еще работает, например:

public class FirstLevelChild<T> extends GenericClass<T> {

}

public class SecondLevelChild extends FirstLevelChild<String> {

}

В этом случае getMyType () = java.lang.String

Лин Ю Чэн
источник
Это не возвращает тип T. Это возвращает T не java.lang.String, кроме того, код не может преобразовать Type в Class <T>
Mohy Eldeen
Вот онлайн-образец, который я сделал. Нажмите compile и выполните, тогда вы можете получить результат. tutorialspoint.com/...
Линь Юй Чэн
У меня работает - когда WildFly Weld CDI сломал альтернативный метод.
Андре
Я получилException in thread "main" java.lang.NullPointerException at Main$ClassA.findTypeArguments(Main.java:54) at Main$ClassA.findTypeArguments(Main.java:54) at Main$ClassA.findTypeArguments(Main.java:54) at Main$ClassA.<init>(Main.java:43) at Main.main(Main.java:61)
yuyang
0
public static final Class<?> getGenericArgument(final Class<?> clazz)
{
    return (Class<?>) ((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments()[0];
}
kayz1
источник
0

Вот мое решение. Примеры должны объяснить это. Единственное требование заключается в том, что подкласс должен устанавливать общий тип, а не объект.

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.HashMap;
import java.util.Map;

public class TypeUtils {

    /*** EXAMPLES ***/

    public static class Class1<A, B, C> {

        public A someA;
        public B someB;
        public C someC;

        public Class<?> getAType() {
            return getTypeParameterType(this.getClass(), Class1.class, 0);
        }

        public Class<?> getCType() {
            return getTypeParameterType(this.getClass(), Class1.class, 2);
        }
    }

    public static class Class2<D, A, B, E, C> extends Class1<A, B, C> {

        public B someB;
        public D someD;
        public E someE;
    }

    public static class Class3<E, C> extends Class2<String, Integer, Double, E, C> {

        public E someE;
    }

    public static class Class4 extends Class3<Boolean, Long> {

    }

    public static void test() throws NoSuchFieldException {

        Class4 class4 = new Class4();
        Class<?> typeA = class4.getAType(); // typeA = Integer
        Class<?> typeC = class4.getCType(); // typeC = Long

        Field fieldSomeA = class4.getClass().getField("someA");
        Class<?> typeSomeA = TypeUtils.getFieldType(class4.getClass(), fieldSomeA); // typeSomeA = Integer

        Field fieldSomeE = class4.getClass().getField("someE");
        Class<?> typeSomeE = TypeUtils.getFieldType(class4.getClass(), fieldSomeE); // typeSomeE = Boolean


    }

    /*** UTILS ***/

    public static Class<?> getTypeVariableType(Class<?> subClass, TypeVariable<?> typeVariable) {
        Map<TypeVariable<?>, Type> subMap = new HashMap<>();
        Class<?> superClass;
        while ((superClass = subClass.getSuperclass()) != null) {

            Map<TypeVariable<?>, Type> superMap = new HashMap<>();
            Type superGeneric = subClass.getGenericSuperclass();
            if (superGeneric instanceof ParameterizedType) {

                TypeVariable<?>[] typeParams = superClass.getTypeParameters();
                Type[] actualTypeArgs = ((ParameterizedType) superGeneric).getActualTypeArguments();

                for (int i = 0; i < typeParams.length; i++) {
                    Type actualType = actualTypeArgs[i];
                    if (actualType instanceof TypeVariable) {
                        actualType = subMap.get(actualType);
                    }
                    if (typeVariable == typeParams[i]) return (Class<?>) actualType;
                    superMap.put(typeParams[i], actualType);
                }
            }
            subClass = superClass;
            subMap = superMap;
        }
        return null;
    }

    public static Class<?> getTypeParameterType(Class<?> subClass, Class<?> superClass, int typeParameterIndex) {
        return TypeUtils.getTypeVariableType(subClass, superClass.getTypeParameters()[typeParameterIndex]);
    }

    public static Class<?> getFieldType(Class<?> clazz, AccessibleObject element) {
        Class<?> type = null;
        Type genericType = null;

        if (element instanceof Field) {
            type = ((Field) element).getType();
            genericType = ((Field) element).getGenericType();
        } else if (element instanceof Method) {
            type = ((Method) element).getReturnType();
            genericType = ((Method) element).getGenericReturnType();
        }

        if (genericType instanceof TypeVariable) {
            Class<?> typeVariableType = TypeUtils.getTypeVariableType(clazz, (TypeVariable) genericType);
            if (typeVariableType != null) {
                type = typeVariableType;
            }
        }

        return type;
    }

}
PetermaxX
источник
-1

Я сделал то же самое, что и @Moesio Above, но в Котлине это можно сделать так:

class A<T : SomeClass>() {

    var someClassType : T

    init(){
    this.someClassType = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] as Class<T>
    }

}
Bonestack
источник
-4

Это может быть полезно для кого-то. Вы можете использовать java.lang.ref.WeakReference; сюда:

class SomeClass<N>{
  WeakReference<N> variableToGetTypeFrom;

  N getType(){
    return variableToGetTypeFrom.get();
  }
}
TheLetch
источник
Как этот класс должен использоваться? Почему WeakReference? Пожалуйста, предоставьте некоторое объяснение с вашим ответом, а не просто код.
Абхиджит Саркар
Так что, если у вас есть, SomeClass<MyClass> вы можете создать экземпляр SomeClassи вызвать getTypeэтот экземпляр и иметь время выполнения MyClass.
TheLetch
Конечно, но почему WeakReference? То, что вы сказали, не отличается от большинства других ответов.
Абхиджит Саркар
Во-первых, мой подход короче (меньше кода), во-вторых, Слабые ссылки не препятствуют тому, чтобы их референты были сделаны финализируемыми, и, насколько я знаю, он не использует отражение, поэтому он быстрый
TheLetch
Это не получает тип чего - либо, это возвращает объект этого типа, которые, к вашему сведению, вы можете сделать с практически любым видом обертки ( AtomicReference, List, Set).
Фронтар
-5

Я обнаружил, что это простое и понятное решение

public class GenericClass<T> {

    private Class classForT(T...t) {
        return t.getClass().getComponentType();
    }

    public static void main(String[] args) {
        GenericClass<String> g = new GenericClass<String>();

        System.out.println(g.classForT());
        System.out.println(String.class);
    }
}
Марк Карчнер
источник
Объясните (T...t). (Вот почему этот код не работает.)
Кристофер Шнайдер