Подтвердить что ты не робот

Как определить класс родового типа?

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

Пример:

public class MyGenericClass<T> {
  public void doSomething() {
    // Snip...
    // Call to a 3rd party lib
    T bean = (T)someObject.create(T.class);
    // Snip...
  }
}

Очевидно, что приведенный выше пример не работает и приводит к следующей ошибке: Недопустимый литерал класса для параметра типа T.

Мой вопрос: кто-то знает хорошую альтернативу или обходное решение для этого?

4b9b3361

Ответ 1

Все те же проблемы: общая информация удаляется во время выполнения, ее невозможно восстановить. Обходным путем является передача класса T в параметр статического метода:

public class MyGenericClass<T> {

    private final Class<T> clazz;

    public static <U> MyGenericClass<U> createMyGeneric(Class<U> clazz) {
        return new MyGenericClass<U>(clazz);
    }

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

    public void doSomething() {
        T instance = clazz.newInstance();
    }
}

Это уродливо, но оно работает.

Ответ 2

Я просто указал на это решение:

import java.lang.reflect.ParameterizedType;

public abstract class A<B> {
    public Class<B> g() throws Exception {
        ParameterizedType superclass =
            (ParameterizedType) getClass().getGenericSuperclass();

        return (Class<B>) superclass.getActualTypeArguments()[0];
    }
}

Это работает, если A задан конкретным типом подкласса:

new A<String>() {}.g() // this will work

class B extends A<String> {}
new B().g() // this will work

class C<T> extends A<T> {}
new C<String>().g() // this will NOT work

Ответ 3

К сожалению, решение Кристофа, как написано, работает только в очень ограниченных обстоятельствах. [EDIT: как было сказано ниже, я больше не помню свои рассуждения для этого предложения, и это, скорее всего, неверно: "Обратите внимание, что это будет работать только в абстрактных классах, прежде всего".] Следующая трудность заключается в том, что g() работает только с DIRECT подклассы A. Мы можем это исправить, хотя:

private Class<?> extractClassFromType(Type t) throws ClassCastException {
    if (t instanceof Class<?>) {
        return (Class<?>)t;
    }
    return (Class<?>)((ParameterizedType)t).getRawType();
}

public Class<B> g() throws ClassCastException {
    Class<?> superClass = getClass(); // initial value
    Type superType;
    do {
        superType = superClass.getGenericSuperclass();
        superClass = extractClassFromType(superType);
    } while (! (superClass.equals(A.class)));

    Type actualArg = ((ParameterizedType)superType).getActualTypeArguments()[0];
    return (Class<B>)extractClassFromType(actualArg);
}

Это будет работать во многих ситуациях на практике, но не ВСЕ. Рассмотрим:

public class Foo<U,T extends Collection<?>> extends A<T> {}

(new Foo<String,List<Object>>() {}).g();

Это вызовет ClassCastException, потому что аргумент типа здесь вообще не является Class или ParameterizedType; это TypeVariable T. Итак, теперь вы застряли, пытаясь понять, какой тип T должен был стоять, и так далее вниз по кроличьей дыре.

Я думаю, что единственный разумный общий ответ - это нечто похожее на исходный ответ Николаса - в общем, если вашему классу необходимо создать объекты какого-либо другого класса, который неизвестен во время компиляции, пользователям вашего класса необходимо передать это класса (или, возможно, a Factory) в ваш класс явно и не полагаться исключительно на дженерики.

Ответ 4

Я найду другой способ получить класс общего объекта

public Class<?> getGenericClass(){
         Class<?> result =null;
         Type type =this.getClass().getGenericSuperclass();

         if(type instanceofParameterizedType){
              ParameterizedType pt =(ParameterizedType) type;
              Type[] fieldArgTypes = pt.getActualTypeArguments();
              result =(Class<?>) fieldArgTypes[0];
        }
        return result;
  }

Ответ 5

T можно легко решить с помощью TypeTools:

Class<T> t = (Class<T>) TypeResolver.resolveRawArguments(
                                MyGenericClass.class, getClass());

Ответ 6

Я расскажу о решении Кристофа.

Вот абстрактный класс ClassGetter:

private abstract class ClassGetter<T> {
    public final Class<T> get() {
        final ParameterizedType superclass = (ParameterizedType)
            getClass().getGenericSuperclass();
        return (Class<T>)superclass.getActualTypeArguments()[0];
    }
}

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

public static <T> Class<T> getGenericClass() {
    return new ClassGetter<T>() {}.get();
}

В качестве примера использования этого метода вы можете сделать этот метод:

public static final <T> T instantiate() {
    final Class<T> clazz = getGenericClass();
    try {
        return clazz.getConstructor((Class[])null).newInstance(null);
    } catch (Exception e) {
        return null;
    }
}

И затем используйте его следующим образом:

T var = instantiate();