Отримати загальний тип класу під час виконання


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;
     }
}

58
Мені подобається ця відповідь, але це трохи громіздко: - GenericClass <AbodyClass> g = новий GenericClass <Другий клас> (AnotherClass.class);
Елізео Окампос

1
Це ще більш багатослівно, якщо ви використовуєте підхід дао / завод / менеджер. 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). Трохи приємніше.
Joeri Hendrickx

262

Я бачив щось подібне

private Class<T> persistentClass;

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

у прикладі сплячого загального ДаніAccessObjects


84
Ця методика працює там, де параметр типу визначений у безпосередньому надкласі, але він не вдається, якщо параметр типу визначений в іншому місці в ієрархії типів. Для обробки складніших випадків можна використовувати щось на зразок TypeTools . Документи включають приклад більш досконалого загального DAO.
Джонатан

25
Це повертає лише фактичні параметри типу, які використовуються, коли CLASS реалізує / розширює те, що має загальні декларації, не повертає фактичні параметри типу, що використовуються, коли ІНСТАНЦІЯ інстанціюється. Іншими словами, він МОЖЕ сказати, що в class A implements Comparable<String>, фактичний параметр типу є String, але НЕ МОЖЕ сказати, що в Set<String> a = new TreeSet<String>(), фактичний параметр типу є String. Фактично, інформація про параметри типу "стирається" після компіляції, як пояснено в інших відповідях.
Сіу Чін Понг -Asuka Kenji-

32
Я отримую java.lang.Class cannot be cast to java.lang.reflect.ParameterizedTypeза цю відповідь.
Томаш Зато - Відновіть Моніку

4
Такого підходу можна досягти, використовуючи Class-Mateлюдей Джексона. Я написав історію
yunspace

5
@ TomášZato Зателефонувавши просто вищевказаний код повернув для мене той самий виняток. Я знаю, що це трохи пізно, але в будь-якому випадку мені довелося зателефонувати, (Class<T>) ((ParameterizedType)getClass().getSuperclass().getGenericSuperclass()).getActualTypeArguments()щоб перейти до фактичних аргументів типу.
AndrewMcCoist

98

Дженерики НЕ матеріалізований під час виконання. Це означає, що інформація відсутня під час виконання.

Додавання дженериків до Java під час збереження відсталої сумісності було справжньою умовою (ви можете ознайомитись з настільним документом про це: Зробити майбутнє безпечним для минулого: додавши щедрість мові програмування Java ).

Існує багата література на цю тему, і деякі люди незадоволені сучасним станом, деякі кажуть, що насправді це приманка і в цьому немає реальної потреби. Ви можете прочитати обидва посилання, я вважав їх досить цікавими.


179
Звичайно, ми незадоволені, .NET має набагато кращий загальний механізм обробки
Pacerier

6
@Pacerier: але поправка лише генерики не виведе Java на рівень .NET. Типи значень спеціалізований код для них є, щонайменше, однаково важливим, чому .NET краще в області генерики.
Йоахім Зауер

@JoachimSauer, так, типи значень. Я завжди хотів, щоб вони були в Яві. До речі, що ви маєте на увазі під спеціалізованим кодом?
Pacerier

3
@ spaaarky21 Ні, параметри загального типу видаляються під час компіляції (так зване "стирання", ви можете перейти в Google). Хитрість у відповіді 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
Cody A. Ray

3
@ CodyA.Ray Ваш код кидає а java.lang.IllegalArgumentException: class com.google.common.reflect.TypeToken isn't parameterized. Тому я змінив лінію new TypeToken(getClass()) { }на new TypeToken<T>(getClass()) { }. Тепер код працює нормально, але Тип все ще 'T'. Дивіться це: gist.github.com/m-manu/9cda9d8f9d53bead2035
Ману

3
@Dominik Перегляньте оновлений приклад, який ви можете скопіювати та вставити, щоб перевірити себе. Я також додав примітку, що пояснює, що ви повинні створити екземпляр підкласу (як показано). Як радить загальний етикет, будь ласка, прочитайте будь-які пов’язані статті та пов’язані з цим javadocs, перш ніж звинуватити плакат у "бажаному мисленні". Я використовував подібний виробничий код кілька разів. Помічники Guava, які я демонструю, призначені саме для цього конкретного випадку використання, і їхні 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;

9
private final Class<T> type;
naXa

Як я можу створити з нього тип масиву:Type t = //String[]
Pawel Cioch

@PawelCioch java.lang.reflect.Array.newInstance (тип елемента, довжина); сподіваюся, що це допоможе (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 в Class <*> і отримати genericSuperclass. Переважно в петлі.
фупдук

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();
    }
}

9
Я отримав "Виняток у потоці" main "java.lang.ClassCastException: java.lang.Class не можна передавати в java.lang.reflect.ParameterizedType" з цим зразком коду
yuyang

16

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

З Документів Oracle:

Тип стирання

Generics були представлені на мові Java, щоб забезпечити більш чіткі перевірки типу під час компіляції та підтримати загальне програмування. Для реалізації дженерики компілятор Java застосовує стирання типу для:

Замініть всі параметри типу на загальні типи їх межами або Об'єктом, якщо параметри типу не обмежені. Тому створений байт-код містить лише звичайні класи, інтерфейси та методи. Якщо потрібно, вставте роли типів, щоб зберегти безпеку типу. Створити мостові методи для збереження поліморфізму у розширених родових типах. Стирання типу забезпечує відсутність нових класів для параметризованих типів; отже, генерики не вимагають накладних витрат.

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


Юп, це неможливо. Яві потрібні були б перероблені дженерики, щоб це працювало.
Геннінг

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

Я думаю, є ще одне елегантне рішення.

Що ви хочете зробити, це (безпечно) "передати" тип параметра загального типу вгору від класу concerete до надкласу.

Якщо ви дозволяєте мислити тип класу як "метадані" в класі, це пропонує метод 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) у ДВОХ місцях, а не в одному, який не є DRY.
  2. Це можливо лише в тому випадку, якщо ви можете змінити конкретні підкласи.

Так, це не DRY, однак він чистіший, ніж запропоновано вище. Мені це сподобалося. спасибі
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
Мій код не є точним рішенням питання. Він повертає параметри загального типу класу, але не фактичний тип Т. Але це може бути корисним для інших, хто натикається на питання і шукає мого рішення.
Маттіас М

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. Потрібно розширити типізованим класом ( Child extends Generic<Integer>)
АБО

2. Створювати як анонімну реалізацію ( new Generic<Integer>() {};)


4

Ви не можете. Якщо ви додасте до класу змінну типу T (вам навіть не потрібно ініціалізувати її), ви можете використовувати її для відновлення типу.


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;
    }
}

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());
    }
}

3

Щоб завершити деякі відповіді тут, мені довелося отримати параметризований тип 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());
    }
}

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();
    }

}

1
Примітка: це не працює, коли Tє змінною типу. У випадку, що Tє змінною типу, varargs створює масив стирання T. Див., Наприклад, http://ideone.com/DIPNwd .
Radiodef

1
Це повертає "Об'єкт"
yahya

Можливо, ви намагаєтесь відповісти на якесь інше запитання 🤔
Хан

1

На всякий випадок, якщо ви використовуєте зберігання змінної за допомогою загального типу, ви можете легко вирішити цю проблему, додавши метод 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 not java.lang.String, окрім коду, не зможе приховати Тип до класу <T>
Mohy Eldeen

Ось онлайн-зразок, який я зробив. Клацніть компілювати та виконати, тоді ви зможете отримати результат. 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];
}

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;
    }

}

-1

Я робив те саме, що і @Moesio нагорі, але в Котліні це можна зробити так:

class A<T : SomeClass>() {

    var someClassType : T

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

}

-4

Можливо, комусь це стане в нагоді. Ви можете використовувати java.lang.ref.WeakReference; сюди:

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

  N getType(){
    return variableToGetTypeFrom.get();
  }
}

Як слід використовувати цей клас? Чому WeakReference? Будь ласка, надайте пояснення зі своєю відповіддю, а не лише якийсь код.
Abhijit Sarkar

Отже, якщо у вас є, SomeClass<MyClass> ви можете створити екземпляр SomeClassі викликати getTypeцей екземпляр і мати час виконання MyClass.
TheLetch

Звичайно, але чому WeakReference? Те, що ви сказали, не відрізняється від більшості інших відповідей.
Абхіджіт Саркар

По-перше, мій підхід коротший (менше коду), по-друге, слабкі посилання не заважають референтам зробити їх доопрацьованими, і, наскільки я знаю, він не використовує рефлексію, таким чином, це швидко
TheLetch

Це не отримує тип чого - або, це повертає об'єкт цього типу, які, до вашого відома, ви можете зробити з практично будь-яким видом обгортки ( AtomicReference, List, Set).
Frontear

-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). (Ось чому цей код не працює.)
Крістофер Шнайдер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.