How can I determine the type of a generic field in Java?

前端 未结 7 853
无人共我
无人共我 2020-11-27 13:25

I have been trying to determine the type of a field in a class. I\'ve seen all the introspection methods but haven\'t quite figured out how to do it. This is going to be use

相关标签:
7条回答
  • 2020-11-27 14:03

    I haven't found any framework who determines a generic field type through the inheritance layers so i've written some method:

    This logic determines the type through the field information and the current object class.

    Listing 1 - logic:

    public static Class<?> determineType(Field field, Object object) {
        Class<?> type = object.getClass();
        return (Class<?>) getType(type, field).type;
    }
    
    protected static class TypeInfo {
        Type type;
        Type name;
    
        public TypeInfo(Type type, Type name) {
            this.type = type;
            this.name = name;
        }
    
    }
    
    private static TypeInfo getType(Class<?> clazz, Field field) {
        TypeInfo type = new TypeInfo(null, null);
        if (field.getGenericType() instanceof TypeVariable<?>) {
            TypeVariable<?> genericTyp = (TypeVariable<?>) field.getGenericType();
            Class<?> superClazz = clazz.getSuperclass();
    
            if (clazz.getGenericSuperclass() instanceof ParameterizedType) {
                ParameterizedType paramType = (ParameterizedType) clazz.getGenericSuperclass();
                TypeVariable<?>[] superTypeParameters = superClazz.getTypeParameters();
                if (!Object.class.equals(paramType)) {
                    if (field.getDeclaringClass().equals(superClazz)) {
                        // this is the root class an starting point for this search
                        type.name = genericTyp;
                        type.type = null;
                    } else {
                        type = getType(superClazz, field);
                    }
                }
                if (type.type == null || type.type instanceof TypeVariable<?>) {
                    // lookup if type is not found or type needs a lookup in current concrete class
                    for (int j = 0; j < superClazz.getTypeParameters().length; ++j) {
                        TypeVariable<?> superTypeParam = superTypeParameters[j];
                        if (type.name.equals(superTypeParam)) {
                            type.type = paramType.getActualTypeArguments()[j];
                            Type[] typeParameters = clazz.getTypeParameters();
                            if (typeParameters.length > 0) {
                                for (Type typeParam : typeParameters) {
                                    TypeVariable<?> objectOfComparison = superTypeParam;
                                    if(type.type instanceof TypeVariable<?>) {
                                        objectOfComparison = (TypeVariable<?>)type.type;
                                    }
                                    if (objectOfComparison.getName().equals(((TypeVariable<?>) typeParam).getName())) {
                                        type.name = typeParam;
                                        break;
                                    }
                                }
                            }
                            break;
                        }
                    }
                }
            }
        } else {
            type.type = field.getGenericType();
        }
    
        return type;
    }
    

    Listing 2 - Samples / Tests:

    class GenericSuperClass<E, T, A> {
        T t;
        E e;
        A a;
        BigDecimal b;
    }
    
    class GenericDefinition extends GenericSuperClass<Integer, Integer, Integer> {
    
    }
    
    @Test
    public void testSimpleInheritanceTypeDetermination() {
        GenericDefinition gd = new GenericDefinition();
        Field field = ReflectionUtils.getField(gd, "t");
        Class<?> clazz = ReflectionUtils.determineType(field, gd);
        Assert.assertEquals(clazz, Integer.class);
        field = ReflectionUtils.getField(gd, "b");
        clazz = ReflectionUtils.determineType(field, gd);
        Assert.assertEquals(clazz, BigDecimal.class);
    }
    
    class MiddleClass<A, E> extends GenericSuperClass<E, Integer, A> { }
    
    // T = Integer, E = String, A = Double
    class SimpleTopClass extends MiddleClass<Double, String> { }
    
    @Test
    public void testSimple2StageInheritanceTypeDetermination() {
        SimpleTopClass stc = new SimpleTopClass();
        Field field = ReflectionUtils.getField(stc, "t");
        Class<?> clazz = ReflectionUtils.determineType(field, stc);
        Assert.assertEquals(clazz, Integer.class);
        field = ReflectionUtils.getField(stc, "e");
        clazz = ReflectionUtils.determineType(field, stc);
        Assert.assertEquals(clazz, String.class);
        field = ReflectionUtils.getField(stc, "a");
        clazz = ReflectionUtils.determineType(field, stc);
        Assert.assertEquals(clazz, Double.class);
    }
    
    class TopMiddleClass<A> extends MiddleClass<A, Double> { }
    
    // T = Integer, E = Double, A = Float
    class ComplexTopClass extends TopMiddleClass<Float> {}
    
    @Test void testComplexInheritanceTypDetermination() {
        ComplexTopClass ctc = new ComplexTopClass();
        Field field = ReflectionUtils.getField(ctc, "t");
        Class<?> clazz = ReflectionUtils.determineType(field, ctc);
        Assert.assertEquals(clazz, Integer.class);
        field = ReflectionUtils.getField(ctc, "e");
        clazz = ReflectionUtils.determineType(field, ctc);
        Assert.assertEquals(clazz, Double.class);
        field = ReflectionUtils.getField(ctc, "a");
        clazz = ReflectionUtils.determineType(field, ctc);
        Assert.assertEquals(clazz, Float.class);
    }
    
    class ConfusingClass<A, E> extends MiddleClass<E, A> {}
    // T = Integer, E = Double, A = Float ; this class should map between a and e
    class TopConfusingClass extends ConfusingClass<Double, Float> {}
    
    @Test
    public void testConfusingNamingConvetionWithInheritance() {
        TopConfusingClass tcc = new TopConfusingClass();
        Field field = ReflectionUtils.getField(tcc, "t");
        Class<?> clazz = ReflectionUtils.determineType(field, tcc);
        Assert.assertEquals(clazz, Integer.class);
        field = ReflectionUtils.getField(tcc, "e");
        clazz = ReflectionUtils.determineType(field, tcc);
        Assert.assertEquals(clazz, Double.class);
        field = ReflectionUtils.getField(tcc, "a");
        clazz = ReflectionUtils.determineType(field, tcc);
        Assert.assertEquals(clazz, Float.class);
        field = ReflectionUtils.getField(tcc, "b");
        clazz = ReflectionUtils.determineType(field, tcc);
        Assert.assertEquals(clazz, BigDecimal.class);
    }
    
    class Pojo {
        Byte z;
    }
    
    @Test
    public void testPojoDetermineType() {
        Pojo pojo = new Pojo();
        Field field = ReflectionUtils.getField(pojo, "z");
        Class<?> clazz = ReflectionUtils.determineType(field, pojo);
        Assert.assertEquals(clazz, Byte.class);
    }
    

    I'm looking forward to hear your feedback!

    0 讨论(0)
提交回复
热议问题