web-dev-qa-db-ja.com

Javaでジェネリックフィールドのタイプを判別するにはどうすればよいですか?

私はクラス内のフィールドのタイプを判別しようとしています。私はすべてのイントロスペクションの方法を見てきましたが、それを行う方法を十分に理解していません。これは、Javaクラスからxml/jsonを生成するために使用されます。ここでいくつかの質問を見てきましたが、必要なものが正確に見つかりませんでした。

例:

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

しかし、これはListListではなく、Personであることだけを教えてくれます

ありがとう

34
Juan Mendes

Java Tutorial Trail:The Reflection API )の Obtaining Field Types をご覧ください。

基本的に、あなたがする必要があるのは、クラスのすべて Java.lang.reflect.Field を取得することです そして、それぞれに対して Field#getType() を呼び出します (以下の編集を確認してください)。 allパブリック、保護、パッケージ、プライベートアクセスフィールドを含むすべてのオブジェクトフィールドを取得するには、単に 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() 、返された TypeParameterizedTypeかどうかを確認し、パラメータを取得しますそれに応じて。 ParameterizedType#getRawType() および ParameterizedType#getActualTypeArgument() を使用して、raw引数とtypes引数の配列を取得します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
55
Pascal Thivent

ここに私の質問に答える例があります

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

この出力

フィールド名:name 
-field type:class Java.lang.String 
 field name:children 
-raw type:interface Java.util.List 
-type arg:class com.blah.Person 
5
Juan Mendes

継承層を介して一般的なフィールドタイプを決定するフレームワークが見つからなかったため、いくつかのメソッドを記述しました。

このロジックは、フィールド情報と現在のオブジェクトクラスを介してタイプを決定します。

リスト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
javaBeCool

このスニペットを取ります:

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

キークラスは Field

3
dfa

これが私の見解です。考えられるすべてのケースを処理することはできません(そして確かにいくつかのバグがあります)が、これまでに私のコードで発生したすべてのケースを処理します。これにはこれらの宣言が含まれており、多くのユースケースにとって良い出発点になります。

  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();
    }
  }
2
Reto Höhener

Dfaが指摘しているように、Java.lang.reflect.Field.getTypeで消去されたタイプを取得できます。 Field.getGenericType(ワイルドカード、バインドされたジェネリックパラメーター、あらゆる種類のクレイジーが含まれる場合があります)を使用してジェネリック型を取得できます。 Class.getDeclaredFieldsを介してフィールドを取得できます(Class.getFieldsは公開フィールド(supertpyeのフィールドを含む)を提供します-無意味です)。基本型フィールドを取得するには、Class.getSuperclassを実行します。 Field.getModifiersから修飾子をチェックすることに注意してください-静的フィールドはおそらくあなたにとって興味深いものではないでしょう。