Javaでクラス属性をループする方法は?


87

Javaのクラス属性を動的にループするにはどうすればよいですか。

例:

public class MyClass{
    private type1 att1;
    private type2 att2;
    ...

    public void function(){
        for(var in MyClass.Attributes){
            System.out.println(var.class);
        }
    }
}

これはJavaで可能ですか?

回答:


101

あなたが求めていることをするための言語的サポートはありません。

リフレクションを使用して、実行時にタイプのメンバーにリフレクティブにアクセスできます(たとえばClass.getDeclaredFields()、の配列を取得するためField)が、実行しようとしていることによっては、これが最善の解決策ではない場合があります。

も参照してください

関連する質問


これは、リフレクションが実行できることの一部のみを示す簡単な例です。

import java.lang.reflect.*;

public class DumpFields {
    public static void main(String[] args) {
        inspect(String.class);
    }
    static <T> void inspect(Class<T> klazz) {
        Field[] fields = klazz.getDeclaredFields();
        System.out.printf("%d fields:%n", fields.length);
        for (Field field : fields) {
            System.out.printf("%s %s %s%n",
                Modifier.toString(field.getModifiers()),
                field.getType().getSimpleName(),
                field.getName()
            );
        }
    }
}

上記のスニペットは、リフレクションを使用して、class String;の宣言されたすべてのフィールドを検査します。次の出力が生成されます。

7 fields:
private final char[] value
private final int offset
private final int count
private int hash
private static final long serialVersionUID
private static final ObjectStreamField[] serialPersistentFields
public static final Comparator CASE_INSENSITIVE_ORDER

効果的なJava2nd Edition、アイテム53:リフレクションよりもインターフェースを優先する

これらは本からの抜粋です:

与えられたClassオブジェクト、あなたが得ることができConstructorMethodおよびFieldクラスのコンストラクタ、メソッドやフィールドを表すインスタンス。[彼らは]あなたに彼らの根底にある対応物を反射的に操作させます。ただし、この力には代償が伴います。

  • コンパイル時チェックのすべての利点が失われます。
  • リフレクティブアクセスを実行するために必要なコードは、不器用で冗長です。
  • パフォーマンスが低下します。

原則として、実行時に通常のアプリケーションでオブジェクトに反射的にアクセスしないでください。

リフレクションを必要とする洗練されたアプリケーションがいくつかあります。例としては、[...意図的に省略...]アプリケーションがこれらのカテゴリのいずれかに該当するかどうかについて疑問がある場合は、おそらく該当しません。


実際、フィールドの値に応じて、各フィールドの値を書き込みたいのか、書きたくないのですか?
ザカリア2010

@Zakaria:はい、リフレクションでこれを達成できますが、何をしようとしているのかによって、はるかに優れたデザインがあります。
多遺伝子

実際、クラスから生成するレポートがあります。クラスフィールドの値に応じて、それらのフィールドを配置するかどうかに応じて、実装できる最適な設計は何ですか。
ザカリア2010

1
@Zakaria:先に進んで、振り返ってみてください。Field.getフィールドの値を反射的に読み取るために使用できるものがあります。もしそうならprivate、あなたはそれを回避することができるかもしれませんsetAccessible。はい、リフレクションは非常に強力で、privateフィールドの検査、フィールドの上書きfinalprivateコンストラクターの呼び出しなどを行うことができます。設計の側面についてさらに支援が必要な場合は、より多くの情報を含む新しい質問を作成する必要があります。おそらく、そもそもこれらの多くの属性は必要ありません(たとえば、useenumListなど)。
多遺伝子潤滑剤2010

1
ジェネリックはここでは役に立ちません。ただやるstatic void inspect(Class<?> klazz)
user102008 2011

45

フィールドに直接アクセスすることは、Javaではあまり良いスタイルではありません。Beanのフィールドにgetterメソッドとsetterメソッドを作成してから、java.beansパッケージのIntrospectorクラスとBeanInfoクラスを使用することをお勧めします。

MyBean bean = new MyBean();
BeanInfo beanInfo = Introspector.getBeanInfo(MyBean.class);
for (PropertyDescriptor propertyDesc : beanInfo.getPropertyDescriptors()) {
    String propertyName = propertyDesc.getName();
    Object value = propertyDesc.getReadMethod().invoke(bean);
}

クラスではなく、Beanのプロパティのみを取得する方法はありますか?つまり、getPropertyDescriptorsによって返されるMyBean.classを回避します
hbprotoss 2016年

@hbprotoss正しく理解している場合は、を確認propertyDesc.getReadMethod().getDeclaringClass() != Object.classするか、のような2番目のパラメーターとして分析を停止するクラスを指定できますgetBeanInfo(MyBean.class, Object.class)
ジョーンHorstmann

20

クラスがJavaBeabs仕様に準拠している場合は、Jörnの回答に同意しますが、準拠しておらず、Springを使用している場合は、次の方法が適しています。

SpringにはReflectionUtilsという名前のクラスがあり、doWithFields(class、callback)などの非常に強力な機能を提供します。これは、次のようなコールバックオブジェクトを使用してクラスフィールドを反復処理できるビジタースタイルのメソッドです。

public void analyze(Object obj){
    ReflectionUtils.doWithFields(obj.getClass(), field -> {

        System.out.println("Field name: " + field.getName());
        field.setAccessible(true);
        System.out.println("Field value: "+ field.get(obj));

    });
}

しかし、ここに警告があります:クラスは「内部使用のみ」とラベル付けされています。これは私に尋ねると残念です


14

クラスフィールドを反復処理し、オブジェクトから値を取得する簡単な方法:

 Class<?> c = obj.getClass();
 Field[] fields = c.getDeclaredFields();
 Map<String, Object> temp = new HashMap<String, Object>();

 for( Field field : fields ){
      try {
           temp.put(field.getName().toString(), field.get(obj));
      } catch (IllegalArgumentException e1) {
      } catch (IllegalAccessException e1) {
      }
 }

おそらくクラスまたは任意のオブジェクト。
リッキー

@Rickeyとにかく、各フィールドをループして属性に新しい値を設定する方法はありますか?
hyperfkcb

@hyperfkcb繰り返し処理しているプロパティ/フィールドの値を変更しようとすると、変更例外が発生する場合があります。
リッキー

6

JavaにはReflection(java.reflection。*)がありますが、Apache Beanutilsのようなライブラリを調べることをお勧めします。これにより、リフレクションを直接使用するよりもプロセスの負担が大幅に軽減されます。


0

これは、プロパティをアルファベット順に並べ替えて、それらすべてを値と一緒に出力するソリューションです。

public void logProperties() throws IllegalArgumentException, IllegalAccessException {
  Class<?> aClass = this.getClass();
  Field[] declaredFields = aClass.getDeclaredFields();
  Map<String, String> logEntries = new HashMap<>();

  for (Field field : declaredFields) {
    field.setAccessible(true);

    Object[] arguments = new Object[]{
      field.getName(),
      field.getType().getSimpleName(),
      String.valueOf(field.get(this))
    };

    String template = "- Property: {0} (Type: {1}, Value: {2})";
    String logMessage = System.getProperty("line.separator")
            + MessageFormat.format(template, arguments);

    logEntries.put(field.getName(), logMessage);
  }

  SortedSet<String> sortedLog = new TreeSet<>(logEntries.keySet());

  StringBuilder sb = new StringBuilder("Class properties:");

  Iterator<String> it = sortedLog.iterator();
  while (it.hasNext()) {
    String key = it.next();
    sb.append(logEntries.get(key));
  }

  System.out.println(sb.toString());
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.