LoginSignup
4
4

More than 5 years have passed since last update.

【Java】列挙型(enum)のリフレクションによる動的メソッド呼び出し

Last updated at Posted at 2017-06-08

はじめに

システム改修のお仕事であるが、久しぶりのJavaである。環境はJava7となっている。

とあるテーブルの列でFugaName1~FugaName4となっているのをFugaName1~FugaName50まで拡張することになった。
そこまで増やすなら別テーブルにした方がいいのでは提案はしてみたが、使用状況からするとテーブルを分けるまでもないとのこと。

改修前

ファイル出力するようで下記のようなEnumクラスが作成されていた。
2017/06/11にコンストラクタを追記しました。

Enumクラス
public class Hoge {
    public String no;
    public String fuganame1;
    public String fuganame2;
    public String fuganame3;
    public String fuganame4;

    // コンストラクタ 
    public Hoge(String _no, HashMap<String, String> resultList) {
        no = _no;
        fuganame1 = resultList.get(dataParams.FugaName1.getColNamePhysical());   
        fuganame2 = resultList.get(dataParams.FugaName2.getColNamePhysical());   
        fuganame3 = resultList.get(dataParams.FugaName3.getColNamePhysical());   
        fuganame4 = resultList.get(dataParams.FugaName4.getColNamePhysical());   
    }

    public enum dataParams{
          No         ("no"        , "No"         , 0)
        , FugaName1  ("fuganame1" , "Fuga Name1" , 1)
        , FugaName2  ("fuganame2" , "Fuga Name2" , 2)
        , FugaName3  ("fuganame3" , "Fuga Name3" , 3)
        , FugaName4  ("fuganame4" , "Fuga Name4" , 4);

        private final String colNamePhysical;   //物理名
        private final String colNameLogical;    //論理名
        private final int colIdx;

        private dataParams( String physical , String logical, int index) {
            colNamePhysical = physical;
            colNameLogical = logical;
            colIdx = index;
        }

        public String getColNamePhysical() {
            return colNamePhysical;
        }
        public String getColNameLogical() {
            return colNameLogical;
        }
        public int getColIdx() {
            return colIdx;
        }
    }
 }

4つ程度なのでベタに書かれている。

使用箇所
String fugaName1 = (String)Hoge.dataParams.FugaName1.getColNamePhysical();
addMap.put(fmColumnName, fugaName1);
resultList.add(addMap);
String fugaName2 = (String)Hoge.dataParams.FugaName2.getColNamePhysical();
addMap.put(fmColumnName, fugaName2);
resultList.add(addMap);
String fugaName3 = (String)Hoge.dataParams.FugaName3.getColNamePhysical();
addMap.put(fmColumnName, fugaName3);
resultList.add(addMap);
String fugaName4 = (String)Hoge.dataParams.FugaName4.getColNamePhysical();
addMap.put(fmColumnName, fugaName4);
resultList.add(addMap);

実験

まだ4つならベタでも許容範囲であるが、さすがに50個も書くというのはプログラマーとしてイケてないと思うのと、他にも同じようなことをしているのが複数あるようなので、今後のことを考えてリフレクションに挑戦してみた。
リフレクションを使うのは初めてだったので、確認用のプログラムを作る。

実験
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Test {

    public static void main(String []args) {

        Enum<?> clz = Enum.valueOf(Hoge.dataParams.class, "FugaName2");
        Object[] consts = clz.getClass().getEnumConstants();
        Class<?> sub = consts[0].getClass();
        Method mth = sub.getDeclaredMethod("getColNameLogical"); //getColNamePhysical");
        String val = (String) mth.invoke(consts[clz.ordinal()]);

        System.out.println(val); // 結果: Fuga Name2
    }
}

改修後

Enumクラスについては、とりあえずベタに50まで増やした。
使用箇所についてはリフレクションにて対応した。

使用箇所
Class<?> clz = Hoge.dataParams.class;
Object[] consts = clz.getEnumConstants();
Class<?> sub = consts[0].getClass();

try {
    Method mth = sub.getDeclaredMethod("getColNamePhysical");
    for(int i=1; i<=50; i++){
        Enum<?> v = Enum.valueOf(Hoge.dataParams.class, String.format("FugaName%d", i));
        String fugaName = (String) mth.invoke(consts[v.ordinal()]);
        addMap.put(fmColumnName, fugaName);
        resultList.add(addMap);
    }
} catch (NoSuchMethodException|SecurityException|IllegalAccessException|InvocationTargetException ex) {
    throw ex;
}

追記 2017/06/11

saka1029さんからコメントを頂き、リフレクションしない方法を習得しました。

使用箇所
for (Hoge.dataParams v : Hoge.dataParams.values()) {
    if (!v.toString().matches("FugaName\\d+"))
        continue;
    String fugaName = v.getColNamePhysical();
    addMap.put(fmColumnName, fugaName);
    resultList.add(addMap);
}

コンストラクタのところは、getField(フィールド名)を使いフィールド変数を取得しています。
その場合、throws IllegalArgumentException, IllegalAccessException が追加されます。

参照:getFields と getDeclaredFields の違い

Enumクラス
    // コンストラクタ 
    public Hoge(String _no, HashMap<String, String> resultList) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
        no = _no;

        // 下記と同じことをFugaName1-50まで行う
        // fuganame1 = resultList.get(dataParams.FugaName1.getColNamePhysical());
        for (Hoge.dataParams v : Hoge.dataParams.values()) {
            String name = v.toString();
            if (!name.matches("FugaName\\d+"))
                continue;
            Field field = this.getClass().getField(name.toLowerCase());
            field.set(this, resultList.get(v.getColNamePhysical()));
        }
    }

※リフレクションでフィールドを取得した場合、フィールドの順番は仕様として決まっていないとのこと。
参照:Java リフレクションで取得するフィールドの順番

Java API仕様を見ると、確かに順序は決まっていないという記述がありました。
Class#getFields()

この Class オブジェクトが表すクラスまたはインタフェースのすべてのアクセス可能な public フィールドをリフレクトする、Field オブジェクトを保持している配列を返します。返された配列内の要素は、ソートされていたり、特定の順序になっていたりすることはありません。

Javaの標準的な機能ではフィールドの定義順でフィールド情報を取得することはできないので、アノテーションを使うことで順序を規定させる。
参照:アノテーションの値順でフィールドを出力

最後に

本格的にJavaで改修作業するのは初めてなので、本当はもっといい方法があるのかも知れない。

参照

4
4
2

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
4
4