Как определить тип общего поля в Java?
Я пытаюсь определить тип поля в классе. Я видел все методы интроспекции, но не совсем понял, как это сделать. Это будет использоваться для генерации xml/json из класса 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
of Person
s
Спасибо
Ответы
Ответ 1
Посмотрите на получение типов полей из учебного курса Java : API Reflection.
В основном, вам нужно получить все 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
Ответ 2
Вот пример, который отвечает на мой вопрос
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
Ответ 3
Я не нашел фреймворк, который определяет общий тип поля через уровни наследования, поэтому я написал несколько методов:
Эта логика определяет тип через информацию о поле и текущий класс объекта.
Листинг 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);
}
Я с нетерпением жду ваших отзывов!
Ответ 4
возьмите этот фрагмент:
for (Field field : Person.class.getFields()) {
System.out.println(field.getType());
}
класс ключа Field
Ответ 5
Как указывает dfa, вы можете получить стираемый тип с помощью java.lang.reflect.Field.getType
. Вы можете получить общий тип с Field.getGenericType
(который может иметь подстановочные знаки и связанные общие параметры и всевозможные сумасшествия). Вы можете получить поля через Class.getDeclaredFields
(Class.getFields
даст вам общедоступные поля (в том числе и суперпеты) - бессмысленно). Чтобы получить поля базового типа, пройдите Class.getSuperclass
. Примечание для проверки модификаторов от Field.getModifiers
- статические поля, вероятно, вам не интересны.
Ответ 6
Вот мой прием. Он не может обрабатывать все возможные случаи (и, конечно же, имеет некоторые ошибки), но он обрабатывает каждый случай, который имеет место в моем коде. Это включает в себя эти декларации, которые должны стать хорошим началом для многих случаев использования:
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();
}
}