開発業務の品質改善の一環で、FindBugs, CheckStyleの導入、カスタマイズを行っているのですが、次はirenkaの出番です。
Effective Javaでは、端的にいうとtoStringは当該オブジェクトの情報をすべて返すべきというレコメンドがあります。詳しくは本みてください。
Effective Java 第2版 (The Java Series)
- 作者: Joshua Bloch,柴田芳樹
- 出版社/メーカー: ピアソンエデュケーション
- 発売日: 2008/11/27
- メディア: 単行本(ソフトカバー)
- 購入: 66人 クリック: 831回
- この商品を含むブログ (256件) を見る
たとえば、以下のようにtoStringメソッドをオーバーライドしてcommons-langのToStringBuilderで文字列を合成して返すようなことをします。
public class Employee { private String name; private String deptName; public String getDeptName() { return deptName; } public String getName() { return name; } public void setDeptName(String deptName) { this.deptName = deptName; } public void setName(String name) { this.name = name; } @Override public String toString() { return new org.apache.commons.lang.builder.ToStringBuilder(this) .toString(); } }
Hackのコードは以下のようになりました。ちょっと余計なクラスやインターフェイスが登場していますが、rewriteメソッドの方をみてください。
ここでは、toStringがオーバーライドされていない場合に、自動的に上記のようなtoStringメソッドが作られます。toStringメソッドの内部のコードが適切でない場合は書き換えてもirenkaによって上書きされません。クラスを新規で作成した場合も自動的に作られるので、もう忘れることはないですね。
public class ToStringRewriter extends AbstractHack implements ClassRewriter { private static final String METHOD_NAME_TO_STRING = "toString"; /** * @when event.phase = {@link HackPhase#INITIALIZE} */ @Override public void initialize(HackEvent event, DeclarationFactory declarationFactory, ElementFactory elementFactory, LiteralFactory literalFactory, DocFactory docFactory, Filer filer, Messager messager) { super.initialize(event, declarationFactory, elementFactory, literalFactory, docFactory, filer, messager); } /** * ソースコードをリライトします。 * * @when * @param clazz */ public void rewrite(CtClass<?> clazz) { CtMethod<?> myToStringMethod = clazz.getDeclaredMethod(METHOD_NAME_TO_STRING); if (myToStringMethod == null) { // toStringを新たに生成 myToStringMethod = declarationFactory.newMethod(declarationFactory .typeOf(String.class), METHOD_NAME_TO_STRING); CtAnnotationInstance<?> annotation = elementFactory.newAnnotationInstance(declarationFactory .newAnnotation("Override")); // Overrideアノテーションを付与 myToStringMethod.getModifiersAndAnnotations().add(annotation); CtModifier modifier = elementFactory.newModifier(ModifierKind.PUBLIC); // publicのアクセス修飾子を付与 myToStringMethod.getModifiersAndAnnotations().add(modifier); CtClass<ToStringBuilder> toStringBuilder = declarationFactory.classOf(ToStringBuilder.class); // new ToStringBuilder(this) CtNewInstance<?> newInstance = toStringBuilder.newInvocation(clazz.getThis()); CtMethod<?> toStringMethod = toStringBuilder.getMethod(METHOD_NAME_TO_STRING); // .toString() CtMethodInvocation<?> invocation = toStringMethod.newInvocation(); // new ToStringBuilder(this).toString() の 限定式を合成 invocation.getQualifier().substitute(newInstance); // return文を生成 CtReturn<?> ret = elementFactory.newReturn(invocation); // メソッドの本文に追加 myToStringMethod.getBody().getStatements().add(ret); // メソッドをメンバーに追加 clazz.getMembers().add(myToStringMethod); } } }