かとじゅんの技術日誌

技術の話をするところ

Effective Javaの第4章 項目22 非staticのメンバークラスよりstaticのメンバークラスを選ぶをirenkaでやってみる件

非staticメンバークラスは、エンクロージングオブジェクトとは関係のない参照を作ってしまう原因になるので、GCの際にエンクロージングオブジェクトが残ってしまう可能性があります。詳しくは本を読んでくださいw

ちなみに、今書いているHackをGoogle Codeにアップしました。興味がある方はぜひ。

http://code.google.com/p/irenka-commons-hack/source/checkout

ということで、

public class Abc {
	private class Def {
		private class Ghi {
		}
	}
}

public class Abc {
	private static class Def {
		private static class Ghi {
		}
	}
}

に変更してくれるHackを書いてみます。
以下をご覧ください。

/**
 * 第4章 項目22 非staticのメンバークラスよりstaticのメンバークラスを選ぶ
 * 
 * @author kato
 *
 */
public class NonStaticMemberClassRewriter extends AbstractHack implements
	ClassRewriter {

	/**
	 * @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
	 */
	@Override
	public void rewrite(CtClass<?> clazz) {
		if (clazz.getDeclaredScope() != DeclaredScopeKind.TOP_LEVEL) {
			return;
		}
		rewriteStaticModifier(clazz);
	}

	private void rewriteStaticModifier(CtClass<?> clazz) {
		// 当該クラスが処理対象かチェックする
		if (!isTargetClass(getClass(), clazz)) {
			return;
		}
		for (CtDeclaredType<?> type : clazz.getDeclaredMemberTypes()) {
			if (type.getTypeKind() == TypeKind.CLASS) {
				CtClass<?> classType = (CtClass<?>) type;
				// メンバークラスが処理対象で、かつ非staticクラスかどうか
				if (isTargetClass(getClass(), classType)
					&& !classType.getModifiers().contains(ModifierKind.STATIC)) {
					// staticを追加する
					CtModifier modifier =
						elementFactory.newModifier(ModifierKind.STATIC);
					classType.getModifiersAndAnnotations().add(modifier);
				}
				// メンバークラスがさらにメンバークラスを保持しているなら書き換える処理を実行する
				if (classType.getDeclaredMemberTypes().size() > 0) {
					rewriteStaticModifier(classType);
				}
			}
		}
	}
}

デフォルト動作だと、メンバークラスを記述してコンパイルしたときにstaticがなければstaticが自動追加されます。
また、以下のようにしてアノテーションを使ってstaticの自動追加を禁止することもできます。

@SuppressHackings(NonStaticMemberClassRewriter.class)
public class Abc {
	private class Def {
		private class Ghi {
		}
	}

	private class Jkl {
		private class Mno {
		}
	}
}
public class Abc {
	@SuppressHackings(NonStaticMemberClassRewriter.class)
	private class Def {
		private class Ghi {
		}
	}

	private static class Jkl {
		private static class Mno {
		}
	}
}

以上、ご参考までに。
EJ2のハックがだいたいできたら、今度はS2用のHackを書いてみようかな。