Como posso determinar o tipo de um campo genérico em Java?

Eu tenho tentado determinar o tipo de um campo em uma class. Eu vi todos os methods de introspecção, mas não consegui descobrir como fazê-lo. Isso será usado para gerar xml / json a partir de uma class java. Eu olhei algumas das questões aqui, mas não encontrei exatamente o que eu preciso.

Exemplo:

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

Quando eu março este object, eu preciso saber que o campo chidren é uma lista de objects do tipo Person , então eu posso organizá-lo apropriadamente.

Eu tentei

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

Mas isso só vai me dizer que é uma List , não uma List de Person

obrigado

Dê uma olhada em Obtenção de Tipos de Campo na Trilha de Tutorial do Java : A API de Reflexão .

Basicamente, o que você precisa fazer é obter todos os java.lang.reflect.Field da sua class e chamar Field#getType() em cada um deles (veja a edição abaixo). Para obter todos os campos de object, incluindo campos de access públicos, protegidos, pacotes e privados, simplesmente use Class.getDeclaredFields() . Algo assim:

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

EDIT: Como apontado por wowest em um comentário, você realmente precisa chamar Field#getGenericType() , verifique se o Type retornado é um ParameterizedType e, em seguida, pegue os parâmetros de acordo. Use ParameterizedType#getRawType() e ParameterizedType#getActualTypeArgument() para obter o tipo bruto e uma matriz do argumento types de um ParameterizedType respectivamente. O código a seguir demonstra isso:

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

E seria a saída:

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

Aqui está um exemplo que responde a minha pergunta

 class Person { public final String name; public final List 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()); } } 

Esta saídas

 nome do campo: nome
 Tipo de campo: class java.lang.String
 nome do campo: crianças
 -faça o tipo: interface java.util.List
 -type arg: class com.blah.Person

Eu não encontrei nenhum framework que determine um tipo de campo genérico através das camadas de inheritance, então escrevi algum método:

Essa lógica determina o tipo através das informações de campo e da class de object atual.

Listagem 1 – lógica:

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

Listagem 2 – Amostras / Testes:

 class GenericSuperClass { T t; E e; A a; BigDecimal b; } class GenericDefinition extends GenericSuperClass { } @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 extends GenericSuperClass { } // T = Integer, E = String, A = Double class SimpleTopClass extends MiddleClass { } @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 extends MiddleClass { } // T = Integer, E = Double, A = Float class ComplexTopClass extends TopMiddleClass {} @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 extends MiddleClass {} // T = Integer, E = Double, A = Float ; this class should map between a and e class TopConfusingClass extends ConfusingClass {} @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); } 

Estou ansioso para ouvir o seu feedback!

pegue este trecho:

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

a class chave é Field

Como o dfa aponta, você pode obter o tipo apagado com java.lang.reflect.Field.getType . Você pode obter o tipo genérico com Field.getGenericType (que pode ter curingas e parâmetros genéricos vinculados e todos os tipos de loucura). Você pode obter os campos através de Class.getDeclaredFields ( Class.getFields lhe dará campos públicos (incluindo os do supertpye) – sem sentido). Para obter os campos do tipo base, vá até Class.getSuperclass . Nota para verificar os modificadores de Field.getModifiers – os campos estáticos provavelmente não serão interessantes para você.

Aqui está a minha opinião. Ele não pode lidar com todos os casos possíveis (e com certeza tem alguns bugs), mas lida com todos os casos que ocorrem no meu código até o momento. Isso inclui essas declarações, que devem ser um bom começo para muitos casos de uso:

  private int primitiveField1; private Object field1; private List field2; private Map field3; private Map, Object>>> field4; private char[] array1; private Character[] array2; private Class[] array3; private List[] array4; private InnerClass innerClass; 

Implementação:

  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 { // eg WildcardTypeImpl (Class) return genericType.getTypeName(); } }