プログラミング自主学習 DAY65 Annotation/Generic
Annotation(アノテーション)
コンパイラとランタイム時にどのように処理するかを教える設定情報を提供するものだ。
@Overrideはjavaの典型的なアノテーションで、コンパイル時、メソッドの再定義検査をするように設定する。的確に再定義しなければ、コンパイラからエラーが発生する。
Annotationは、@を付けて表現し、Springなどのフレームワークでもよく使わる。
Annotationを直接生成することは少ないが、直接宣言・定義することで理解を深めていく。
アノテーションタイプの定義
public @interface AnnotationName{
}
@AnnotationName
public @interface AnnotationName{
String prop1();
inr prop2() default 1;
}
インターフェースのように、属性を宣言することができ、初期化の際にはdefaultという表現でできる。prop1()の場合、抽象メソッドと似ている表記であり、値を設定していない。
変数を宣言する際に、()を追加したイメージだ。
@AnnotationName(prop1 = "値");
値がない場合、以下のように値を代入することが必修になる。
public @interface AnnotationName{
String value();
inr prop2() default 1;
}
@AnnotationName("値");
@AnnotationName(value = "値", prop2 = 3);
アノテーションは、value
という基本属性を持つことができ、その場合は属性名を記入せずに、値を入れることもできる。一つ以上の属性の値を代入する際は、valueも他の属性のように、
明示的に属性名を記入する必要がある。
アノテーションの適用対象
@Target
を付けることで、適用対象を設定することができる。
ElementType
というEnumの列挙定数から適用対象を適用する。
@Target({ElementType.TYPE, ElementType.FIELD, Element.METHOD})
public @interface AnnotationName{
}
@AnnotationName
public class ClassName
@AnnotationName
private String fieldName;
//@AnnotationName
public ClassName(){}
@AnnotationName
public void methodName(){}
@Targetにコンストラクタは指定いないため、提供できない。
アノテーションの維持
アノテーションを維持するタイミングを決める際には、@Retention を付ける。
RetentionPolicy
の列挙定数にアクセスし、呼び出すことができる。
@Target({ElementType.TYPE, ElementType.FIELD, Element.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnotationName{
}
SOURCE : コンパイルした後、消える
CLASS : メモリからローディングされてから消える。
RUNTIME :プログラム終了までずっと維持される。
★アノテーションの利用★
アノテーションは、あくまで設定情報なので、直接検査をする実行情報は持っていない。
そのような、設定情報のチェックはSpringなどのアプリケーションが処理する。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface PrintAnnotation {
String value() default "-";
int number() default 15;
}
以下のように、アノテーションを定義し、適用対象と維持(スコープ)を設定した。
こらから、Serviceというクラスで、@PrintAnnotationを適用し、アノテーションを利用する。
public class Service {
@PrintAnnotation
public void method1() {
System.out.println("1");
}
@PrintAnnotation("*") //value = "*"
public void method2() {
System.out.println("2");
}
@PrintAnnotation(value="#", number=20)
public void method3() {
System.out.println("3");
}
}
import java.lang.reflect.Method;
public class PrintAnnotationExample {
public static void main(String[] args) throws Exception {
Method[] deClareMethods = Service.class.getDeclaredMethods();
for(Method method : deClareMethods) {
//get PrintAnnotation
PrintAnnotation printAnnotation = method.getAnnotation(PrintAnnotation.class);
printLine(printAnnotation);
method.invoke(new Service());
printLine(printAnnotation);
}
}
public static void printLine(PrintAnnotation printAnnotation) {
if(printAnnotation != null) {
//get number's value
int number = printAnnotation.number();
for(int i=0; i<number; i++) {
//get value's value
String value = printAnnotation.value();
System.out.print(value);
}
System.out.println();
}
}
}
####################
method 3
####################
---------------
method 1
---------------
***************
method 2
***************
Generic(ジェネリック)
決定されていない<タイプ>をパラメータに処理し、
実際使用する時に、パラメータを具体的にタイプに代替する機能。
public class Box<T> {
public T content
}
Boxクラスでまだ決定されていないcontentのタイプをTというタイプパラメータに定義した。
まだ、Tには何が入るかは分からないが、オブジェクトを生成する際にはcontentが他のタイプに変わることのみ分かる。
Box<String> box = new Box<String>();
box.content = "hello";
String content = box.content;
Box<Integer>box = new Box<Integer>();
box.content = 100;
int content = box.content;
このように、オブジェクトを生成する際に、ほかのクラスタイプを入れれば、タイプパラメータTの変わりに記入したタイプに代替できる。
ジェネリックを活用することで、キャストやinstanceof演算子はいらなくなった。
public class Box<T> {
public T content;
}
public class GenericExample {
public static void main(String[] args) {
Box<String> box1 = new Box<>();
box1.content = "hello";
String str = box1.content;
System.out.println(str);
Box<Integer> box2 = new Box<>();
box2.content = 100;
int value = box2.content;
System.out.println(value);
}
}
hello
100
ラッパークラスと相性が抜群だ。
Discussion