6

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

5

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

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

public class GenericClass<T>
{
    public Type getMyType()
    {
        // Как мне вернуть тип T?
    }
}

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

5 ответ(ов)

4

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

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

public class GenericClass<T> {

     private final Class<T> type;

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

     public Class<T> getMyType() {
         return this.type;
     }
}

Этот подход позволяет сохранить информацию о типе в момент создания объекта вашей обобщенной класса.

0

В Java обобщения (generics) в основном работают на этапе компиляции, что означает, что информация о типах теряется во время выполнения программы.

Рассмотрим следующий пример:

class GenericCls<T> {
    T t;
}

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

class GenericCls {
    Object o;
}

Для того чтобы сохранить информацию о типе во время выполнения, необходимо передать её в качестве аргумента конструктору:

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;

Таким образом, благодаря передаче класса String.class в конструктор GenericCls, мы можем сохранить информацию о типе и получить её в любой момент через метод getType().

0

Конечно, вы можете это сделать.

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

Согласно официальной документации:

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

Тем не менее, для вашей ситуации я бы не стал использовать рефлексию. Лично я предпочитаю использовать её для кода фреймворков. В вашем случае я бы просто добавил тип в качестве параметра конструктора.

0

В вашем коде есть абстрактный класс AbstractDao, который использует дженерики для определения типа сущности, с которой он работает. Конструктор этого класса извлекает фактический тип параметра T с помощью рефлексии. Если вы хотите сделать это в контексте ответа на StackOverflow, вот как это может выглядеть:


Вам нужно реализовать абстрактный класс с дженериками, чтобы получать тип сущности во время выполнения? Ваш код выглядит почти правильно, но может вызвать предупреждение о "unchecked cast". Вот исправленная версия:

import java.lang.reflect.ParameterizedType;

public abstract class AbstractDao<T> {
    private final Class<T> persistentClass;

    @SuppressWarnings("unchecked")
    public AbstractDao() {
        this.persistentClass = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass())
                .getActualTypeArguments()[0];
    }

    public Class<T> getPersistentClass() {
        return persistentClass;
    }
}

Обратите внимание на аннотацию @SuppressWarnings("unchecked"), которая помогает избежать предупреждений компилятора о небезопасном приведении типов. Этот подход позволяет вам в будущем использовать persistentClass для создания экземпляров вашего типа T или для других целей, связанных с рефлексией.

Если у вас есть дополнительные вопросы по использованию этого подхода, не стесняйтесь спрашивать!

0

В вашем коде вы используете подход с параметризованными типами для получения класса параметра T, который будет определён при наследовании. Давайте разберём ваш код по частям.

Вы создаёте класс A<T>, который имеет защищённое поле clazz, хранящее информацию о классе, который был передан как параметр типа T. Конструктор класса пытается получить фактический тип параметра, используя ParameterizedType и метод getGenericSuperclass(). Это позволяет вам динамически определять класс T в конструкторе.

Когда вы создаёте класс B, который наследует A<C>, конструктор A выполнит код и корректно определит, что clazz является экземпляром Class<C>. Таким образом, вызвав метод getClazz() в методе anything() класса B, вы сможете работать с Class<C>.

Вот полный пример вашего кода:

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() {
        // Здесь я могу использовать getClazz(), что вернёт Class<C>
        Class<C> clazzOfC = getClazz();
        // Дальше вы можете использовать clazzOfC для рефлексии или других целей
    }
}

Таким образом, ваш код позволяет вам получить класс параметра при помощи рефлексии, что может быть полезно в различных сценариях, например, при работе с коллекциями, сериализацией и т.д. Если у вас есть дополнительные вопросы по этому коду или другим аспектам Java, не стесняйтесь задавать!

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь