J'ai essayé de déterminer le type d'un champ dans une classe. J'ai vu toutes les méthodes d'introspection mais je n'ai pas tout à fait compris comment le faire. Cela va être utilisé pour générer du xml/json à partir d'une classe Java. J'ai examiné un certain nombre de questions ici mais je n'ai pas trouvé exactement ce dont j'avais besoin.
Exemple:
class Person {
public final String name;
public final List<Person> children;
}
Lorsque je rassemble cet objet, j'ai besoin de savoir que le champ chidren
est une liste d'objets de type Person
, donc je peux le marshaller correctement.
J'avais essayé
for (Field field : Person.class.getDeclaredFields()) {
System.out.format("Type: %s%n", field.getType());
}
Mais cela me dira seulement que c'est un List
, pas un List
de Person
s
Merci
Jetez un œil à Obtention des types de champs à partir du Java Tutorial Trail: The Reflection API .
Fondamentalement, ce que vous devez faire est d'obtenir tous Java.lang.reflect.Field
de votre classe et appelez (Cochez Modifier ci-dessous). Pour obtenir tous les champs d'objet , y compris les champs d'accès public, protégé, package et privé, utilisez simplement Field#getType()
sur chacun d'euxClass.getDeclaredFields()
. Quelque chose comme ça:
for (Field field : Person.class.getDeclaredFields()) {
System.out.format("Type: %s%n", field.getType());
System.out.format("GenericType: %s%n", field.getGenericType());
}
EDIT: Comme indiqué par wowest dans un commentaire, vous devez en fait appeler Field#getGenericType()
, vérifiez si le Type
renvoyé est un ParameterizedType
, puis saisissez les paramètres en conséquence. Utilisez ParameterizedType#getRawType()
et ParameterizedType#getActualTypeArgument()
pour obtenir le type brut et un tableau de l'argument types d'un ParameterizedType
respectivement. Le code suivant illustre cela:
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());
}
}
Et produirait:
Field: name - Type: class Java.lang.String
Field: children - Raw type: interface Java.util.List - Type args: class foo.Person
Voici un exemple qui répond à ma question
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());
}
}
Cette sorties
nom de champ: nom - type de champ: classe Java.lang.String nom de champ: enfants - type brut: interface Java.util.List - tapez arg: classe com.blah.Person
Je n'ai trouvé aucun cadre qui détermine un type de champ générique à travers les couches d'héritage, j'ai donc écrit une méthode:
Cette logique détermine le type via les informations de champ et la classe d'objets actuelle.
Listing 1 - logique:
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 - Échantillons/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);
}
J'ai hâte d'entendre vos commentaires!
prenez cet extrait:
for (Field field : Person.class.getFields()) {
System.out.println(field.getType());
}
la classe de clé est Field
Voici mon point de vue. Il ne peut pas gérer tous les cas possibles (et a sûrement quelques bugs), mais il gère tous les cas qui se produisent dans mon code jusqu'à présent. Cela inclut ces déclarations, qui devraient être un bon début pour de nombreux cas d'utilisation:
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;
La mise en oeuvre:
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();
}
}
Comme le souligne dfa, vous pouvez obtenir le type effacé avec Java.lang.reflect.Field.getType
. Vous pouvez obtenir le type générique avec Field.getGenericType
(qui peut avoir des caractères génériques et des paramètres génériques liés et toutes sortes de folies). Vous pouvez obtenir les champs via Class.getDeclaredFields
(Class.getFields
vous donnera des champs publics (y compris ceux du supertpye) - inutiles). Pour obtenir les champs de type de base, passez par Class.getSuperclass
. Remarque pour vérifier les modificateurs de Field.getModifiers
- les champs statiques ne vous intéresseront probablement pas.