guice タイプセーフな annotatedWith

Google guice で JSR330 の @Named に対して、
バインド定義で @Namedアノテーションがついていることを限定する場合、

binder().bind(Logic.class).annotatedWith(Names.named("HIGH")).to(HighLogic.class);

と、com.google.inject.name.Namesnamed(String) メソッドで、文字列を渡すように書かなければならず、
誤って想定しない文字列を書いてしまうと実行時までバグが気付かず、
タイプセーフとなっていない。

列挙型をアノテーションで指定させよう→ アノテーションと共にアノテーション実装を用意してバインド判定に
使えるように
しなくてはならない。

という敷居の高さもでてくるが、1つこのパターンでアノテーション実装を書いておけば
流用できるだろう。

public enum Level{
    HIGH, MIDDLE, LOW;
}

この列挙型 Level を指定するアノテーションを以下のように、アノテーションの実装とともに
用意する。
アノテーションを implements したクラス実装定義を、アノテーションの定義で書いてしまう。

import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import com.google.inject.BindingAnnotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/**
 * PointLevel
 */
@Retention(RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
@BindingAnnotation
public @interface PointLevel {
    Level value();

    class Implemention implements PointLevel, Serializable{
        private static final long serialVersionUID = 0;
        private final Level value;

        public static Implemention post(Level level){
            return new Implemention(level);
        }
        Implemention(Level value) {
            this.value = value;
        }
        @Override
        public Level value() {
            return this.value;
        }
        @Override
        public int hashCode() {
            return (127 * "value".hashCode()) ^ value.hashCode();
        }
        @Override
          public String toString() {
            return "@" + PointLevel.class.getName();
        }
        @Override
        public Class<? extends Annotation> annotationType() {
            return PointLevel.class;
        }
    }
}

↑ ここで、hashCode実装を怠ってはならない!


インジェクション先は、以下のように書く。

@Inject @PointLevel(Level.MIDDLE) private Logic alogic;

Guice の Module で、バインド定義は、例えば以下のように annotatedwith で
生成インスタンスを限定する。

binder().bind(Logic.class).annotatedWith(PointLevel.Implemention.post(Level.HIGH))
.to(HighLogic.class);
binder().bind(Logic.class).annotatedWith(PointLevel.Implemention.post(Level.MIDDLE))
.to(MiddleLogic.class);
binder().bind(Logic.class).annotatedWith(PointLevel.Implemention.post(Level.LOW))
.to(LowLogic.class);