commons-langでtoStringメソッドをも〜っと簡単に実装する
ReflectionToStringBuilder
ReflectionToStringBuilderは、前回のToStringBuilderと同様、toStringメソッドの実装を簡単にするためのメソッドである。
今回もテスト用コードとその実行結果から見てみよう。
public class ReflectionToStringBuilderTest {
private String testStr;
private boolean testBoolean;
private int testInt;
private String[] testArray;
public String toString() {
return new ReflectionToStringBuilder(this).toString();
}
public static void main(String[] args) {
// インスタンスを生成して出力する
ReflectionToStringBuilderTest test = new ReflectionToStringBuilderTest();
System.out.println(test);
// フィールド値をセットして再度出力する
test.setTestStr("ABC");
test.setTestBoolean(true);
test.setTestInt(1);
test.setTestArray(new String[]{"aaa","bbb"});
System.out.println(test);
}
// 以下アクセサ(getter/setter)は省略
処理内容は前回と同じである。ただし、toStringメソッドの実装で「ToStringBuilder」ではなく、「ReflectionToStringBuilder」を使用している。
mainメソッドを実行した結果は以下のようになる。
ReflectionToStringBuilderTest@fe748f[testStr=,testBoolean=false,testInt=0,testArray= ] ReflectionToStringBuilderTest@fe748f[testStr=ABC,testBoolean=true,testInt=1,testArray={aaa,bbb}]
クラス名以外は前回と同じ結果が出力されていることがわかるだろう。
ReflectionToStringBuilderを使用する場合、ToStringBuilderを使用する場合とは異なりフィールドの定義を個別に指定する必要がなく、極めて簡単にtoStringメソッドを実装できる。
フィールドに変更があった場合でもtoStringメソッドを修正する必要がないため保守性も良い。
また、以下のように記述することで任意のフィールドを除外することができる。「基本的に全てのフィールドを出力するが、一部のフィールドのみtoStringメソッドの結果に含みたくない」という場合に有効である。
public String toString() {
return new ReflectionToStringBuilder(this)
.setExcludeFieldNames(new String[]{"testStr"})
.toString();
}
この場合の実行結果は以下のようになる。
ReflectionToStringBuilderTest@fe748f[testBoolean=false,testInt=0,testArray=] ReflectionToStringBuilderTest@fe748f[testBoolean=true,testInt=1,testArray={aaa,bbb}]
setExcludeFieldNamesメソッドで指定した「testStr」の情報が出力されていないことを確認できる。
toStringの結果をログに出力する可能性を考えれば、会員のパスワードのような項目はこの方法で除外するのが良いだろう(情報セキュリティの観点からいえば、この種の情報をログに出力するのは好ましくないため)。
ここまで読んで、ReflectionToStringBuilderがあればToStringBuilderは必要ないのではないか?と思われた方がいるかもしれない。しかし、そうではないのだ。
ReflectionToStringBuilderは内部でリフレクションを使用するため、ToStringBuilderに比べて速度的に劣るのだ。
速度がどの程度異なるか検証してみよう。
以下の内容はtoStringメソッドを10万回実行して、その処理時間を出力するコードだ。
ReflectionToStringBuilderTest test = new ReflectionToStringBuilderTest();
long start = System.currentTimeMillis();
for(int i=0;i<100000;i++){
test.toString();
}
long end = System.currentTimeMillis();
System.out.println(end-start);
筆者の環境では、このコードの実行時間は900ms前後になった。それに対して、ToStringBuilderを使って実装した場合の実行時間は400ms前後である。
10万回実行してこの程度の差であれば問題になることはまずないだろう。しかし、クラスのフィールドの数を追加したり、除外フィールドを指定することで処理時間の差はさらに開いていく。
筆者は基本的にReflectionToStringBuilderを使用しており、状況に応じてToStringBuilderの使用を検討している。
判断のポイントとしては以下が挙げられる。
- 運用環境で頻繁にログに出力するか(toStringメソッドが呼ばれるか)
- ログに出力する必要のないフィールドの数が多いか
- 除外フィールドの数が多いか
早いもので、この連載も今回が最終回となる。全10回の連載では、筆者が使う機会の多い機能を中心に紹介してきたが、紙面の都合で今回取り上げることのできなかった便利な機能はまだある。
続きはこの記事を読んでいるあなたが研究してみて欲しい。この連載を通してcommons-langに興味を持ってもらえたら幸いだ。
- ホワイトペーパー



