Как получить класс переменной типа в 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
, который используется в приведенном выше коде:
Клиенты создают пустой анонимный подкласс. При этом этот параметр вводится в иерархию типов анонимного типа, поэтому мы можем восстановить его во время выполнения, несмотря на стирание.