2008-10-08 2 views
56

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

Пример:

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

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

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

ответ

45

Все те же проблемы: общая информация удаляется во время выполнения, ее невозможно восстановить. Обойти это передать класс 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(); 
    } 
} 

Это некрасиво, но это работает.

+0

Это, безусловно, уродливо, но трюк с защищенным конструктором для меня достаточно хорош. Я использую общий класс как абстрактный класс, который служит базой от 5 до 10 конкретных классов. Благодаря! – 2008-10-08 13:36:16

+0

Можно ли использовать это, чтобы определить, какой общий тип является объектом? например `if (obj.getClazz() является строкой) doThis(); if (obj.getClazz() - целое число) doThat(); ` – dwjohnston 2012-11-09 00:09:36

+1

^ ` if (obj.getClazz(). Equals (String.class)) ... ` – dwjohnston 2012-11-09 01:14:32

21

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

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 
4

К сожалению решение Кристофа как написано работает только в очень ограниченных обстоятельствах , [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 вообще; это TypeVariableT. Так что теперь вы застряли, пытаясь понять, какой тип должен был стоять T, и так далее вниз по кроличьей дыре.

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

2

я найти другой способ, чтобы получить класс родового объекта

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; 
    } 
0

T может быть решена довольно легко с помощью TypeTools:

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

Я остановлюсь на решение Кристофа.

Вот 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(); 
Смежные вопросы