Javaでクラスの全フィールド(メンバ変数)を取得するために試行錯誤した過程を備忘録として残します。
やりたいこと
下記クラスの全フィールドをIteratorで取得したい。
具体的には 「field1、field2、field3、field4」 が取得できること。
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及びBeanUtilsはJavaBeansを扱うクラスです。1
その中でもbean内の各値をMapに変換して返却するdescribe(Object bean)メソッドを利用して、
- BeanをMapに変換
- 変換したMapのkey値を取得(フィールド名)
という流れで全フィールドを取得してみることにしました。
実装例
// 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);
}
}
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クラスの配列で取得できます。
実装例
// 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();
}
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でも、全フィールドを取得することができます。
-
DynaValidatorFormまたはDynaActionFormを継承したクラスを作成 -
getMapメソッドを利用して、フォームをMapに変換 → 変換したMapのkey値を取得(フィールド名)を実装する
StrutsのDynaActionFormには、DynaActionFormで定義したプロパティをMap型に変換して返却するgetMapメソッドが存在します。
これにより試したこと1と同様の考え方で、全フィールドを取得することができます。
package test;
import java.util.Iterator;
import org.apache.struts.validator.DynaValidatorForm;
public class StrutsDynaValidatorForm extends DynaValidatorForm {
public Iterator getNames() {
return getMap().keySet().iterator();
}
}
-
BeanUtilsの使い方に関して参考になるサイトはこちら。Java BeanUtilsメモ(Hishidama's commons-BeanUtils Memo) ↩