2009-12-08 4 views
29

Я пытался определить тип поля в классе. Я видел все методы интроспекции, но не совсем понял, как это сделать. Это будет использоваться для генерации xml/json из класса java. Я рассмотрел ряд вопросов здесь, но не нашел точно, что мне нужно.Как определить тип общего поля в Java?

Пример:

class Person { 
    public final String name; 
    public final List<Person> children; 
} 

Когда я маршал этого объект, мне нужно знать, что chidren поле представляет собой список объектов типа Person, так что я могу маршал это правильно.

Я попытался

for (Field field : Person.class.getDeclaredFields()) { 
    System.out.format("Type: %s%n", field.getType()); 
} 

Но это сказать мне только, что это List, не List из Person сек

Благодаря

+1

Список ? Если я не упущу смысл. – Woot4Moo

+0

То есть я имел в виду –

ответ

50

Посмотрите Obtaining Field Types из Java Tutorial Trail: The Reflection API ,

В принципе, то, что вам нужно сделать, это получить все java.lang.reflect.Field вашего класса и вызвать Field#getType() на каждом из них (проверьте редактирование ниже). Чтобы получить все поля объектов, включая общедоступные, защищенные, пакеты и приватные поля доступа, просто используйте Class.getDeclaredFields(). Что-то вроде этого:

for (Field field : Person.class.getDeclaredFields()) { 
    System.out.format("Type: %s%n", field.getType()); 
    System.out.format("GenericType: %s%n", field.getGenericType()); 
} 

EDIT: Как отметил wowest в комментарии, вы на самом деле нужно вызвать Field#getGenericType(), проверьте, если возвращаемый Type является ParameterizedType, а затем захватить параметры соответственно. Используйте ParameterizedType#getRawType() и ParameterizedType#getActualTypeArgument(), чтобы получить необработанный тип и массив аргументов типов ParameterizedType соответственно. Следующий код демонстрирует это:

for (Field field : Person.class.getDeclaredFields()) { 
    System.out.print("Field: " + field.getName() + " - "); 
    Type type = field.getGenericType(); 
    if (type instanceof ParameterizedType) { 
     ParameterizedType pType = (ParameterizedType)type; 
     System.out.print("Raw type: " + pType.getRawType() + " - "); 
     System.out.println("Type args: " + pType.getActualTypeArguments()[0]); 
    } else { 
     System.out.println("Type: " + field.getType()); 
    } 
} 

И будет выход:

Field: name - Type: class java.lang.String 
Field: children - Raw type: interface java.util.List - Type args: class foo.Person 
+4

Паскаль - ты очень близко. вы хотите Тип type = Field.getGenericType(); Затем проверьте, является ли это параметризованным типом, затем захватите параметры. Это в конечном итоге является кроличьей дырой - ассер должен определить пределы или быть готовым написать какой-то довольно причудливый код. – wowest

+0

Спасибо, wowest, это именно то, что мне нужно в сочетании с ParameterizedType.getRawType и ParameterizedType.getActualArguments. –

+0

Я не могу понять, как отметить ваш комментарий как правильный ответ, поэтому я просто отвечу на свой вопрос, чтобы опубликовать пример –

3

принять этот фрагмент:

for (Field field : Person.class.getFields()) { 
     System.out.println(field.getType()); 
} 

ключа класса является Field

+0

Это общедоступные поля (включая статические) с 'Class.getFields'. Также будет стертый тип. –

+0

то я неправильно понял вопрос – dfa

1

Как DFA указует, вам может получить стертый тип с java.lang.reflect.Field.getType. Вы можете получить общий тип с Field.getGenericType (который может иметь подстановочные знаки и связанные общие параметры и всевозможные сумасшествия). Вы можете получить поля через Class.getDeclaredFields (Class.getFields предоставит вам публичные поля (в том числе и суперпеты) - бессмысленно). Чтобы получить поля базового типа, перейдите по Class.getSuperclass. Примечание для проверки модификаторов от Field.getModifiers - статические поля, вероятно, вам не интересны.

4

Вот пример, который отвечает на мой вопрос

class Person { 
    public final String name; 
    public final List<Person> children; 
} 

//in main 
Field[] fields = Person.class.getDeclaredFields(); 
for (Field field : fields) { 
    Type type = field.getGenericType(); 
    System.out.println("field name: " + field.getName()); 
    if (type instanceof ParameterizedType) { 
    ParameterizedType ptype = (ParameterizedType) type; 
    ptype.getRawType(); 
    System.out.println("-raw type:" + ptype.getRawType()); 
    System.out.println("-type arg: " + ptype.getActualTypeArguments()[0]); 
    } else { 
    System.out.println("-field type: " + field.getType()); 
    } 
} 

Это выводит

 
field name: name 
-field type: class java.lang.String 
field name: children 
-raw type:interface java.util.List 
-type arg: class com.blah.Person 
+0

Downvoter, если вы объясните нисходящее направление, я могу улучшить ответ. –

4

я не нашел какие-либо рамок, которые определяют общий тип поля через наследование слои, так что я написал некоторые метод:

Эта логика определяет тип через информацию о поле и текущий класс объекта.

Листинг 1 - логика:

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

Листинг 2 - Образцы/Тесты:

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

Я с нетерпением жду, чтобы услышать ваше мнение!

+0

Спасибо! Этот метод очень помог. Я бы рекомендовал добавить, проверяют ли тип.type экземпляр 'ParameterizedTypeImpl', и в этом случае метод' .getRawType() 'дает тип. Это приведет к выборке 'List', когда type будет' List '. (иначе он может получить 'ParameterizedTypeImpl не может быть отброшен в java.lang.Class') –

1

Вот мой прием. Он не может обрабатывать все возможные случаи (и, конечно же, имеет некоторые ошибки), но он обрабатывает каждый случай, который имеет место в моем коде. Это включает в себя такие заявления, которые должны быть хорошим стартом для многих случаев применения:

private int            primitiveField1; 

    private Object            field1; 
    private List<Integer>          field2; 
    private Map<Integer, String>        field3; 
    private Map<? extends String, List<Map<Class<?>, Object>>> field4; 

    private char[]            array1; 
    private Character[]          array2; 
    private Class<? extends Integer>[]       array3; 
    private List<Integer>[]         array4; 

    private InnerClass<String>         innerClass; 

Реализация:

public static String getDeclaration(Field field) { 
    return getDeclaration(field.getGenericType()); 
    } 

    private static String getDeclaration(Type genericType) { 
    if(genericType instanceof ParameterizedType) { 
     // types with parameters 
     ParameterizedType parameterizedType = (ParameterizedType) genericType; 
     String declaration = parameterizedType.getRawType().getTypeName(); 
     declaration += "<"; 

     Type[] typeArgs = parameterizedType.getActualTypeArguments(); 

     for(int i = 0; i < typeArgs.length; i++) { 
     Type typeArg = typeArgs[i]; 

     if(i > 0) { 
      declaration += ", "; 
     } 

     // note: recursive call 
     declaration += getDeclaration(typeArg); 
     } 

     declaration += ">"; 
     declaration = declaration.replace('$', '.'); 
     return declaration; 
    } 
    else if(genericType instanceof Class<?>) { 
     Class<?> clazz = (Class<?>) genericType; 

     if(clazz.isArray()) { 
     // arrays 
     return clazz.getComponentType().getCanonicalName() + "[]"; 
     } 
     else { 
     // primitive and types without parameters (normal/standard types) 
     return clazz.getCanonicalName(); 
     } 
    } 
    else { 
     // e.g. WildcardTypeImpl (Class<? extends Integer>) 
     return genericType.getTypeName(); 
    } 
    } 
+0

Можете ли вы прокомментировать, что может сделать ваш javaBeCool? Интересно, является ли сочетание того и другого необходимым для обработки рекурсивных дженериков и наследования. См. Https://stackoverflow.com/a/19363555/227299 –

+1

@JuanMendes javaBeCool, похоже, касается другого варианта использования. Мой ответ заключается в том, чтобы получить объявление поля в виде строки, я использую его в проекте генерации кода. Мой оригинальный вопрос был отмечен как дубликат этого вопроса - вот почему я разместил здесь (https://stackoverflow.com/q/45083186/1124509). – Zalumon

Смежные вопросы