Получить имя типа для общего параметра родового класса

У меня есть небольшая проблема в Java при использовании универсальности. У меня есть класс A:

public class A<T>

В методе A мне нужно получить имя типа T Есть ли способ найти строку s помощью T?

(Если я создаю A<String> temp = new A<String>(); я хочу иметь возможность получить java.lang.String в один момент - я должен использовать универсальность, потому что один из моих методов должен будет возвращать List<T>).

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

Ответ 1

Вы не можете сделать это вообще из-за стирания стилей - экземпляр A<String> не знает тип T. Если вам это нужно, один из способов - использовать литерал типа:

public class A<T>
{
    private final Class<T> clazz;

    public A<T>(Class<T> clazz)
    {
        this.clazz = clazz;
    }

    // Use clazz in here
}

Тогда:

A<String> x = new A<String>(String.class);

Это уродливо, но то, что стирает стирание: (

Альтернативой является использование чего-то вроде Guice TypeLiteral. Это работает, потому что аргумент типа, используемый для указания суперкласса, не стирается. Итак, вы можете сделать:

A<String> a = new A<String>() {};

a теперь относится к подклассу A<String>, поэтому, получив a.getClass().getSuperClass(), вы можете вернуться к String. Это довольно ужасно, хотя.

Ответ 2

Вы можете получить название дженериков из подкласса. Смотрите этот пример. Определим родительский класс следующим образом:

public class GetTypeParent<T> {

    protected String getGenericName()
    {
        return ((Class<T>) ((ParameterizedType) getClass()
                .getGenericSuperclass()).getActualTypeArguments()[0]).getTypeName();
    }
}

Затем мы определяем его дочерний класс следующим образом:

public class GetTypeChild extends GetTypeParent<Integer> {
    public static void main(String[] args) {
        GetTypeChild getTypeChild = new GetTypeChild();
        System.out.println(getTypeChild.getGenericName());
    }
}

Вы можете видеть, что в методе main или в любом инстанс-методе я способен получить имя универсального типа, в этом случае main напечатает: java.lang.Integer.

Ответ 3

Краткий ответ: невозможно.

Немного более длинный ответ: как только ваш код скомпилирован, параметры типа отбрасываются. Таким образом, Java не может знать, что вы там установили. Вы можете, однако, передать рассматриваемый класс своему объекту и работать с ним:

public class A<T> {
  private final Class<T> clazz;

  A(Class<T> clazz){
     this.clazz = clazz;
  }
...
}

Ответ 4

Это, безусловно, возможно, если аргумент типа задается с помощью подкласса A:

public class B extends A<String> {}

Class<?> typeArg = TypeResolver.resolveRawArgument(A.class, B.class);
assert typeArg == String.class;

TypeResolver доступен через TypeTools.

Ответ 5

Generics в Java реализуется путем стирания, поэтому нет, вы не сможете получить имя типа, которое было использовано для создания вашей общей коллекции во время выполнения. Кроме того, почему бы просто не проверить элементы, чтобы узнать, к какому типу он принадлежит?

Ответ 6

Если вы делаете это в подклассе, который имеет родительский класс, определяющий общий тип, это то, что сработало для меня:

// get generic type class name
String name = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0].toString();

// then when you've got the name, you can make the Class<T> object
Class.forName(name.replace("class ", ""))

Причина, по которой я не мог сделать это с #getClass() вместо #toString() в первом snip, заключается в том, что я всегда получал класс Class, который для меня бесполезен.

Ответ 7

Как обычно, Apache имеет решение для этого с помощью TypeUtils:

https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/reflect/TypeUtils.html

Быстрый пример из вышеуказанного вопроса:

TypeUtils.getTypeArguments(temp.getClass(), A.class).get(A.class.getTypeParameters()[0])

Отказ от ответственности: я не пытался создать это первым, но использовал эту утилиту аналогичным образом в прошлом.