Как получить класс переменной типа в Java Generics

Я видел похожие вопросы, но они очень не помогли.

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

public class ContainerTest<T>
{

    public void doSomething()
    {
        //I want here to determinate the Class of the type argument (In this case String)
    }
}

и другой класс, который использует этот класс контейнеров

public class TestCase
{

    private ContainerTest<String> containerTest;

    public void someMethod()
    {
        containerTest.doSomething();
    }
}

Можно ли определить класс аргумента типа в методе doSomething() без с явным типом переменной/полем или любым конструктором > в классе ContainerTest?

Обновление: Измененный формат класса ContainerTest

Ответы

Ответ 1

Единственный способ - сохранить класс в переменной экземпляра и потребовать его как аргумент конструктора:

public class ContainerTest<T>
{
    private Class<T> tClass;
    public ContainerTest(Class<T> tClass) {
        this.tCLass = tClass;
    }

    public void doSomething()
    {
        //access tClass here
    }
}

Ответ 2

Если вас интересует способ отражения, я нашел частичное решение в этой замечательной статье: http://www.artima.com/weblogs/viewpost.jsp?thread=208860

Короче говоря, вы можете использовать методы java.lang.Class.getGenericSuperclass() и java.lang.reflect.ParameterizedType.getActualTypeArguments(), но вы должны подклассифицировать некоторый родительский суперкласс.

Следующий фрагмент работает для класса, который напрямую расширяет суперкласс AbstractUserType. См. Статью, на которую ссылаются, для более общего решения.

import java.lang.reflect.ParameterizedType;


public class AbstractUserType<T> {

    public Class<T> returnedClass() {
        ParameterizedType parameterizedType = (ParameterizedType) getClass()
                .getGenericSuperclass();

        @SuppressWarnings("unchecked")
        Class<T> ret = (Class<T>) parameterizedType.getActualTypeArguments()[0];

        return ret;
    }

    public static void main(String[] args) {
        AbstractUserType<String> myVar = new AbstractUserType<String>() {};

        System.err.println(myVar.returnedClass());
    }

}

Ответ 3

Нет никакого "чистого" способа получения аргумента Generic Type из класса. Вместо этого, общий шаблон состоит в том, чтобы передать класс Generic Type в конструктор и сохранить его как внутреннее свойство juste, как это сделано в реализации java.util.EnumMap.

http://docs.oracle.com/javase/1.5.0/docs/api/java/util/EnumMap.html http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/EnumMap.java

public class ContainerTest<T> {

    Class<T> type;
    T t;

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

    public void setT(T t) {
        this.t = t;
    }

    public T getT() {
        return t;
    }

    public void doSomething() {
        //There you can use "type" property.
    }
}

Ответ 4

Нет. Это невозможно из-за типа erasure (параметры типа скомпилированы как броски типа Object +). Если вам действительно нужно знать/применять этот тип во время выполнения, вы можете сохранить ссылку на объект Class.

public class ContainerTest<T> {
   private final Class<T> klass;
   private final List<T> list = new ArrayList<T>();

   ContainerTest(Class<T> klass) {
     this.klass = klass;
   }

   Class<T> getElementClass() {
     return klass;
   }

   void add(T t) {
      //klass.cast forces a runtime cast operation
      list.add(klass.cast(t));
   }
}

Использование:

ContainerTest<String> c = new ContainerTest<>(String.class);

Ответ 5

Чтобы узнать значение T, вам нужно записать его в определении типа путем подкласса ContainerTest:

class MyStringClass extends ContainerTest<String> {}

Вы также можете сделать это с помощью анонимного класса, если хотите:

ContainerTest<String> myStringClass = new ContainerTest<String>{}();

Затем, чтобы разрешить значение T, вы можете использовать TypeTools:

Class<?> stringType = TypeResolver.resolveRawArgument(ContainerTest.class, myStringClass.getClass());
assert stringType == String.class;

Ответ 6

Если вы можете определить, как это сделать

public class ContainerTest<T>
{

    public void doSomething(T clazz)
    {

    }
}

Тогда возможно

Ответ 7

Существует способ получить тип выполнения параметра типа с помощью Guava TypeToken для его захвата. Недостатком решения является то, что вам нужно создать анонимный подкласс каждый раз, когда вам нужен экземпляр Container.

class Container<T> {

    TypeToken<T> tokenOfContainedType = new TypeToken<T>(getClass()) {};

    public Type getContainedType() {
        return tokenOfContainedType.getType();
    }
}

class TestCase {

    // note that containerTest is not a simple instance of Container,
    // an anonymous subclass is created
    private Container<String> containerTest = new Container<String>() {};

    @Test
    public void test() {
        Assert.assertEquals(String.class, containerTest.getContainedType());
    }
}

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

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