6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Javaでクラスの全フィールド(メンバ変数)を取得したい時に試したこと

6
Last updated at Posted at 2020-01-12

Javaでクラスの全フィールド(メンバ変数)を取得するために試行錯誤した過程を備忘録として残します。

やりたいこと

下記クラスの全フィールドをIteratorで取得したい。
具体的には 「field1、field2、field3、field4」 が取得できること。

Fields.java
public class Fields {
  
  // フィールド
  private String field1;
  private String field2;
  private String field3;
  private String field4;
  // コンストラクタ
  Fields(String field1, String field2, String field3, String field4) {
    this.field1 = field1;
    this.field2 = field2;
    this.field3 = field3;
    this.field4 = field4;
  }
  // settet,getterは省略
  // .
  // .
  // .
}

試したこと1:Apache CommonsのPropertyUtils及びBeanUtilsを利用する

PropertyUtils及びBeanUtilsJavaBeansを扱うクラスです。1
その中でもbean内の各値をMapに変換して返却するdescribe(Object bean)メソッドを利用して、

  1. BeanをMapに変換
  2. 変換したMapのkey値を取得(フィールド名)

という流れで全フィールドを取得してみることにしました。

実装例

Fields.java
 // PropertyUtils#describeを用いて全フィールドを取得
 public Iterator<?> getNamesForPropertyUtils() {
    try {
        // BeanをMapに変換 → 変換したMapのkey値を取得(フィールド名)
        return PropertyUtils.describe(this).keySet().iterator();
    } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
        System.out.println("フィールドの取得に失敗しました:" + e.toString());
        throw new RuntimeException(e);
    }
  }
  // BeanUtilsUtils#describeを用いて全フィールドを取得
 public Iterator<?> getNamesForBeanUtils() {
    try {
        // BeanをMapに変換 → 変換したMapのkey値を取得(フィールド名)
        return BeanUtils.describe(this).keySet().iterator();
    } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
        System.out.println("フィールドの取得に失敗しました:" + e.toString());
        throw new RuntimeException(e);
    }
  }
FieldGet.java
package test;

import java.lang.reflect.InvocationTargetException;
import java.util.Iterator;

public class FieldGet {
  public static void main (String args[]) {

    Fields field = new Fields("フィールド1","フィールド2","フィールド3","フィールド4");
    Iterator<?> propertyUtils;
    Iterator<?> beanUtils;
    // PropertyUtils#describeを用いて全フィールドを取得
    propertyUtils = field.getNamesForPropertyUtils();
    System.out.println("--PropertyUtils利用--");
    while (propertyUtils.hasNext()) {
        System.out.println(propertyUtils.next());
    }
    // BeanUtilsUtils#describeを用いて全フィールドを取得
    beanUtils = field.getNamesForBeanUtils();
    System.out.println("--BeanUtils利用--");
    while (beanUtils.hasNext()) {
        System.out.println(beanUtils.next());
    }
  }
}

実行結果

実行結果
--PropertyUtils利用--
Field1
Field3
class
Field2
Field4
--BeanUtils利用--
Field1
Field3
class
Field2
Field4

Beanのフィールド名を取得することが出来ましたが、フィールド名ではないclassが取得されたり、順番が昇順でないなど、改善の余地がありそうです。

また、何度か実行しているうちにPropertyUtils/BeanUtils.describe(this)の箇所でInvocationTargetExceptionが発生しました。
解決方法がわからなかったため、別の方法でフィールド名を取得を検討することになりました。

実行結果(一部)
Caused by: java.lang.reflect.InvocationTargetException
  ... 1024 more
Caused by: java.lang.StackOverflowError

試したこと2:リフレクションを利用する

今思えばこれがJavaにおける正攻法だと思うのですが、メンバ変数はリフレクションAPIでも取得できます。2
getDeclaredFields()を用いると、すべてのアクセス修飾子(public、protected、デフォルト、private)のフィールドがFieldクラスの配列で取得できます。

実装例

Fields.java
  // Class#getDeclaredFieldsを用いて全フィールドを取得
  public Iterator<?> getNamesForReflection() {
    // すべてのアクセス修飾子のフィールドを取得
    Field[] tmpField = this.getClass().getDeclaredFields();
    // Iteratorで返却するためのリストを生成
    List<String> fieldNames = new ArrayList<String>();
    for (int i = 0; i < tmpField.length; i++) {
      // getName()でフィールドを取得
      fieldNames.add(tmpField[i].getName());
    }
    return fieldNames.iterator();
  }
FieldGet.java
package test;
import java.lang.reflect.Field;
import java.util.Iterator;

public class FieldGet {
  public static void main (String args[]) {

    Fields field = new Fields("フィールド1","フィールド2","フィールド3","フィールド4");
    Iterator<?> reflection;
    // Class#getDeclaredFieldsを用いて全フィールドを取得
    reflection = field.getNamesForReflection();
    System.out.println("--reflection利用--");
    while (reflection.hasNext()) {
        System.out.println(reflection.next());
    }
  }
}

実行結果

実行結果
--reflection利用--
Field1
Field2
Field3
Field4

これで全フィールドを取得することができました。

【番外編】:StrutsのDynaActionFormで全フィールドを取得

StrutsのDynaActionFormでも、全フィールドを取得することができます。

  1. DynaValidatorFormまたはDynaActionFormを継承したクラスを作成
  2. getMapメソッドを利用して、フォームをMapに変換 → 変換したMapのkey値を取得(フィールド名)を実装する

StrutsのDynaActionFormには、DynaActionFormで定義したプロパティをMap型に変換して返却するgetMapメソッドが存在します。
これにより試したこと1と同様の考え方で、全フィールドを取得することができます。

StrutsDynaValidatorForm.java
package test;

import java.util.Iterator;
import org.apache.struts.validator.DynaValidatorForm;

public class StrutsDynaValidatorForm extends DynaValidatorForm {
  public Iterator getNames() {
    return getMap().keySet().iterator();
  }
}
  1. BeanUtilsの使い方に関して参考になるサイトはこちら。Java BeanUtilsメモ(Hishidama's commons-BeanUtils Memo)

  2. JavaのリフレクションAPIでフィールド取得

6
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?