Извлечение параметра с generic класса
От: avpavlov  
Дата: 17.01.08 08:32
Оценка:
Привет всем!

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

Может кто-нибудь знает как обойти нижеуказанное ограничение?!!

Это тест с примерами использования, а также с примерами ограничений

/**
 * Author: Alexander Pavlov
 */

import java.util.Date;

import junit.framework.TestCase;

public class ClassUtilsTest extends TestCase {

    public static class A<T1, T2> {}
    public static class B<T1, T2> extends A<T2, Long> {}
    public static class C<T1, T2, T3> extends B<T1, T2> {}
    public static class D<T1, T2> extends C<String, T2, T1> {}
    public static class E extends D<Date, Integer> {}
    public static class F extends E {}
    public static class G extends F {}
    public static class H<T1> extends G {}
    public static class I extends H<Double> {}

    public void testGetGenericClassTypeParameter() throws Exception {
        I f1 = new I();
        I f2 = new I() {}; // anonymous

        assertEquals(Double.class, ClassUtils.getGenericClassTypeParameter(H.class, 0, f1));
        assertEquals(Double.class, ClassUtils.getGenericClassTypeParameter(H.class, 0, f2));
        assertEquals(Double.class, ClassUtils.getGenericClassTypeParameter(H.class, "T1", f1));
        assertEquals(Double.class, ClassUtils.getGenericClassTypeParameter(H.class, "T1", f2));

        assertEquals(Date.class, ClassUtils.getGenericClassTypeParameter(D.class, 0, f1));
        assertEquals(Date.class, ClassUtils.getGenericClassTypeParameter(D.class, 0, f2));
        assertEquals(Integer.class, ClassUtils.getGenericClassTypeParameter(D.class, 1, f1));
        assertEquals(Integer.class, ClassUtils.getGenericClassTypeParameter(D.class, 1, f2));
        assertEquals(Date.class, ClassUtils.getGenericClassTypeParameter(D.class, "T1", f1));
        assertEquals(Date.class, ClassUtils.getGenericClassTypeParameter(D.class, "T1", f2));
        assertEquals(Integer.class, ClassUtils.getGenericClassTypeParameter(D.class, "T2", f1));
        assertEquals(Integer.class, ClassUtils.getGenericClassTypeParameter(D.class, "T2", f2));

        assertEquals(String.class, ClassUtils.getGenericClassTypeParameter(C.class, 0, f1));
        assertEquals(String.class, ClassUtils.getGenericClassTypeParameter(C.class, 0, f2));
        assertEquals(Integer.class, ClassUtils.getGenericClassTypeParameter(C.class, 1, f1));
        assertEquals(Integer.class, ClassUtils.getGenericClassTypeParameter(C.class, 1, f2));
        assertEquals(Date.class, ClassUtils.getGenericClassTypeParameter(C.class, 2, f1));
        assertEquals(Date.class, ClassUtils.getGenericClassTypeParameter(C.class, 2, f2));
        assertEquals(String.class, ClassUtils.getGenericClassTypeParameter(C.class, "T1", f1));
        assertEquals(String.class, ClassUtils.getGenericClassTypeParameter(C.class, "T1", f2));
        assertEquals(Integer.class, ClassUtils.getGenericClassTypeParameter(C.class, "T2", f1));
        assertEquals(Integer.class, ClassUtils.getGenericClassTypeParameter(C.class, "T2", f2));
        assertEquals(Date.class, ClassUtils.getGenericClassTypeParameter(C.class, "T3", f1));
        assertEquals(Date.class, ClassUtils.getGenericClassTypeParameter(C.class, "T3", f2));

        assertEquals(String.class, ClassUtils.getGenericClassTypeParameter(B.class, 0, f1));
        assertEquals(String.class, ClassUtils.getGenericClassTypeParameter(B.class, 0, f2));
        assertEquals(Integer.class, ClassUtils.getGenericClassTypeParameter(B.class, 1, f1));
        assertEquals(Integer.class, ClassUtils.getGenericClassTypeParameter(B.class, 1, f2));
        assertEquals(String.class, ClassUtils.getGenericClassTypeParameter(B.class, "T1", f1));
        assertEquals(String.class, ClassUtils.getGenericClassTypeParameter(B.class, "T1", f2));
        assertEquals(Integer.class, ClassUtils.getGenericClassTypeParameter(B.class, "T2", f1));
        assertEquals(Integer.class, ClassUtils.getGenericClassTypeParameter(B.class, "T2", f2));

        assertEquals(Integer.class, ClassUtils.getGenericClassTypeParameter(A.class, 0, f1));
        assertEquals(Integer.class, ClassUtils.getGenericClassTypeParameter(A.class, 0, f2));
        assertEquals(Long.class, ClassUtils.getGenericClassTypeParameter(A.class, 1, f1));
        assertEquals(Long.class, ClassUtils.getGenericClassTypeParameter(A.class, 1, f2));
        assertEquals(Integer.class, ClassUtils.getGenericClassTypeParameter(A.class, "T1", f1));
        assertEquals(Integer.class, ClassUtils.getGenericClassTypeParameter(A.class, "T1", f2));
        assertEquals(Long.class, ClassUtils.getGenericClassTypeParameter(A.class, "T2", f1));
        assertEquals(Long.class, ClassUtils.getGenericClassTypeParameter(A.class, "T2", f2));

        // работает, потому что смотрим параметр базового класса
        assertEquals(Long.class, ClassUtils.getGenericClassTypeParameter(A.class, "T2", new B<String, Long>()));
        // работает, потому что смотрим параметр базового класса (так реально создается объект анонимного класса)
        assertEquals(Long.class, ClassUtils.getGenericClassTypeParameter(B.class, "T2", new B<String, Long>(){}));  
        // не работает, потому что смотрим параметр текущего класса 
        assertEquals(Long.class, ClassUtils.getGenericClassTypeParameter(B.class, "T2", new B<String, Long>()));  
    }
}


Это собственно класс
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.TypeVariable;

/**
 * Author: Alexander Pavlov
 */
public final class ClassUtils {
    private ClassUtils() {}

    public static int getGenericClassTypeParameterIndex(Class genericClass, String name) {
        TypeVariable [] tvars = genericClass.getTypeParameters();
        for (int index = 0; index < tvars.length; index++) {
            if (tvars[index].getName().equals(name)) {
                return index;
            }
        }

        throw new IllegalArgumentException("Class "+genericClass.getSuperclass().getName()+" is not generic or does not contain type parameter "+name);
    }

    public static Class getGenericClassTypeParameter(Class targetGenericClass, String name, Object instance) {
        int index = getGenericClassTypeParameterIndex(targetGenericClass, name);
        return (Class)getGenericClassTypeParameter(targetGenericClass, index, instance.getClass());
    }

    public static Class getGenericClassTypeParameter(Class targetGenericClass, int index, Object instance) {
        return (Class)getGenericClassTypeParameter(targetGenericClass, index, instance.getClass());
    }

    private static Type getGenericClassTypeParameter(Class targetGenericClass, int index, Class instanceClass) {
        if (instanceClass == Object.class) {
            throw new IllegalArgumentException("Could not guess entity class by reflection");
        } else if (instanceClass.getSuperclass() == targetGenericClass) {
            return getGenericClassTypeParameter(instanceClass.getGenericSuperclass(), index);
        } else {
            Type t = getGenericClassTypeParameter(targetGenericClass, index, instanceClass.getSuperclass());
            if (t instanceof Class) {
                return t;
            } else if (t instanceof TypeVariable) {
                int i = getGenericClassTypeParameterIndex(instanceClass.getSuperclass(), ((TypeVariable)t).getName());
                return getGenericClassTypeParameter(instanceClass.getGenericSuperclass(), i);
            } else {
                throw new IllegalArgumentException("Could not guess entity class by reflection");
            }
        }
    }

    private static Type getGenericClassTypeParameter(Type genericClass, int index) {
        if (genericClass instanceof ParameterizedType) {
            ParameterizedType paramType = (ParameterizedType)genericClass;
            return paramType.getActualTypeArguments()[index];
        } else {
            throw new IllegalArgumentException("Could not guess entity class by reflection");
        }
    }

}
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.