はじめに
システム改修のお仕事であるが、久しぶりのJavaである。環境はJava7となっている。
とあるテーブルの列でFugaName1~FugaName4となっているのをFugaName1~FugaName50まで拡張することになった。
そこまで増やすなら別テーブルにした方がいいのでは提案はしてみたが、使用状況からするとテーブルを分けるまでもないとのこと。
改修前
ファイル出力するようで下記のようなEnumクラスが作成されていた。
2017/06/11にコンストラクタを追記しました。
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 の違い
// コンストラクタ
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で改修作業するのは初めてなので、本当はもっといい方法があるのかも知れない。