tablas - ¿Cómo puedo determinar el tipo de campo genérico en Java?
metodo setmodel java (6)
Aquí está mi opinión. No puede manejar todos los casos posibles (y seguramente tiene algunos errores), pero maneja todos los casos que ocurren en mi código hasta el momento. Eso incluye estas declaraciones, que deberían ser un buen comienzo para muchos casos de uso:
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;
Implementación:
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();
}
}
He estado tratando de determinar el tipo de campo en una clase. He visto todos los métodos de introspección pero aún no he descubierto cómo hacerlo. Esto se usará para generar xml / json de una clase java. He visto varias de las preguntas aquí, pero no he encontrado exactamente lo que necesito.
Ejemplo:
class Person {
public final String name;
public final List<Person> children;
}
Cuando marque este objeto, necesito saber que el campo de chidren
es una lista de objetos de tipo Person
, por lo que puedo ordenarlos correctamente.
Había intentado
for (Field field : Person.class.getDeclaredFields()) {
System.out.format("Type: %s%n", field.getType());
}
Pero esto solo me dirá que es una List
, no una List
de Person
Gracias
Aquí hay un ejemplo que responde mi pregunta
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());
}
}
Estas salidas
field name: name -field type: class java.lang.String field name: children -raw type:interface java.util.List -type arg: class com.blah.Person
Como señala dfa, puede obtener el tipo borrado con java.lang.reflect.Field.getType
. Puede obtener el tipo genérico con Field.getGenericType
(que puede tener comodines y parámetros genéricos vinculados y todo tipo de locura). Puede obtener los campos a través de Class.getDeclaredFields
( Class.getFields
le dará campos públicos (incluidos los de la supertpye) - inútil). Para obtener los campos de tipo de base, vaya a Class.getSuperclass
. Nota para comprobar los modificadores de Field.getModifiers
: los campos estáticos probablemente no te interesen.
Eche un vistazo a la Obtención de tipos de campo del Tutorial de Java Trail: The Reflection API .
Básicamente, lo que tienes que hacer es obtener todo java.lang.reflect.Field
de tu clase y llamar a (verifique la edición a continuación). Para obtener todos los campos de objetos, incluidos los campos públicos, protegidos, de paquetes y de acceso privado, simplemente use Field#getType()
en cada uno de ellosClass.getDeclaredFields()
. Algo como esto:
for (Field field : Person.class.getDeclaredFields()) {
System.out.format("Type: %s%n", field.getType());
System.out.format("GenericType: %s%n", field.getGenericType());
}
EDITAR: Como señaló wowest en un comentario, realmente necesita llamar al Field#getGenericType()
, verificar si el Type
devuelto es un tipo ParameterizedType
y luego tomar los parámetros en consecuencia. Use ParameterizedType#getRawType()
y ParameterizedType#getActualTypeArgument()
para obtener el tipo sin ParameterizedType#getActualTypeArgument()
y una matriz del argumento de tipos de ParameterizedType
respectivamente. El siguiente código demuestra esto:
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());
}
}
Y saldría:
Field: name - Type: class java.lang.String
Field: children - Raw type: interface java.util.List - Type args: class foo.Person
No he encontrado ningún marco que determine un tipo de campo genérico a través de las capas de herencia, así que he escrito algún método:
Esta lógica determina el tipo a través de la información de campo y la clase de objeto actual.
Listado 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;
}
Listado 2 - Muestras / Pruebas:
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);
}
¡Espero escuchar tus comentarios!
tomar este fragmento:
for (Field field : Person.class.getFields()) {
System.out.println(field.getType());
}
la clase clave es java.lang.reflect.Field