アノテーション
- クラス・インターフェースなどの型、メンバーに付与できるマーク
- アプリ本来のロジックとは直接関係しない付随情報をコードに追加できる
- 対象となる宣言の先頭に記述
@Override
- 標準ライブラリで提供されるアノテーション
- 対象メソッドが基底クラスのメソッドをオーバーライドしていることを宣言
- 引数のミスでオーバーライドできてない時もコンパイラが警告してくれる
public class Person {
private String firstName;
private String lastName;
public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
@Override
public String toString() {
return String.format("名前は、%s %s です。",
this.lastName, this.firstName);
}
public String getLastName() {
return this.lastName;
}
public String getFirstName() {
return this.firstName;
}
}
@WebServlet
- サーブレットとはサーバー環境でJavaアプリを実行するための基本技術の一種
- サーブレットの基本的な構成情報
- urlPatterns:サーブレットを呼ぶためのURL
- initParams:サーブレットで利用できるパラメータ情報
アノテーションのポイント
- アノテーション入れ子が可能
-
要素1個なら配列の{...}省略可能
urlPatterns = "/init-param"
-
属性名valuesは省略可能
- @WebServlet (values="init-param")
- @WebServlet ("init-param")
-
属性がない場合、()省略可能
- マーカーアノテーション(@Overrideなど)
標準アノテーション
- @Override:基底クラスのメソッドを上書きしていることを宣言
- @Deprecated:クラスやメンバーなどが非推奨であることを宣言
- @SuppressWarnings:コンパイラー警告を抑制
- @SafeVarargs:可変長引数の型安全を宣言
- @FunctionalInterface:インターフェースを関数型インターフェースとして定義することを宣言
@Deprecated
- クラス、メソッドなどが非推奨であることを宣言
- IntegerコンストラクタはJava9以降で非推奨(
since="9"
)- ドキュメンテーションでも記述
- コンパイルは通るが警告がでる
Integer.java
/**
* 中略
* @deprecated
* It is rarely appropriate to use this constructor. The static factory
* {@link #valueOf(int)} is generally a better choice, as it is
* likely to yield significantly better space and time performance.
*/
@Deprecated(since="9")
public Integer(int value) {
this.value = value;
}
@SuppressWarnings
-
特定範囲/種類のコンパイラー警告を非表示
- 他の本来確認すべき警告の見落としを防げる
- 意図して警告対象のコードを利用しているという意思表示
public class AnnotationBasic {
public static Integer process() {
@SuppressWarnings("deprecation")
var i = new Integer(108);
return i;
}
public static void main(String[] args) {
System.out.println(process());
}
}
@SafeVarargs
-
可変長引数の型安全を宣言し、警告が表示されない
- ジェネリック型を可変長引数とすると警告が出る
- あくまでも型安全であることを開発者が認識してるという宣言
自作アノテーション
- アノテーションは修飾子と違って自作できる
- アノテーションは@interface命令で定義
-
@interface配下でアノテーションで利用できる属性を宣言
- 以下の@ClassInfoではバージョン属性をversion属性、value属性で指定する
@ClassInfo(version="2.1", description="アノテーションテスト")
@ClassInfo(version="2.1")
- 以下の@ClassInfoではバージョン属性をversion属性、value属性で指定する
-
アノテーション構成情報宣言するメタアノテーションを記述
- @Documented:javadocにアノテーション情報反映
- @Target:アノテーションをどの要素に適用するか決める
- @Inherited:アノテーションが派生クラスにも継承
- @Retention:アノテーション情報をどのタイミングまで保持するか
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//構成情報宣言
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
//@interface命令でアノテーション定義
public @interface ClassInfo {
//アノテーションで利用できる属性を宣言
String value() default "";
String version() default "";
String description() default "";
}
リフレクションでアノテーション利用
- リフレクション(Reflection):コードの実行中に型情報取得、操作する
- 以下ではClass.forNameメソッドで指定した型情報をClassクラスで取得
- マーカーアノテーション:アノテーションが保持する情報(属性)ではなくアノテーション存在そのものに関心がある
- isAnnotationPresentメソッドでアノテーション有無確認
package mypackage;
//ClassInfoアノテーション指定
@ClassInfo(version = "2.1", description = "アノテーションの動作テスト")
// @ClassInfo("2.1")
public class AnnotationClient {
public static void main(String[] args) throws ClassNotFoundException {
//Classオブジェクトを経由して配下のメンバー取得
var clazz = Class.forName("mypackage.AnnotationClient");
//getAnnotationメソッドでクラスに付与されたアノテーション取得
var info = (ClassInfo)
clazz.getAnnotation(ClassInfo.class);
System.out.println("バージョン:" +
(info.value().equals("") ? info.version() : info.value()));
System.out.println("説明:" + info.description());
}
}
リフレクションのメソッド
オブジェクト生成
- コンストラクタ経由でインスタンス生成
- 呼び出すコンストラクタをClassクラスのgetConstructorメソッドで生成
- newInstanceメソッドで呼び出すことでオブジェクト生成
- Array.newInstanceメソッドで配列生成
- setメソッドで配列要素設定
import java.io.File;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
public class ReflectInstance {
public static void main(String[] args) {
try {
//Fileクラス取得
var clazz = File.class;
//コンストラクタ経由でFileオブジェクト生成
var c = clazz.getConstructor(String.class);
var fl = c.newInstance("./data/data.txt");
System.out.println(fl);
//サイズ2の配列生成
var list = (File[]) Array.newInstance(File.class, 2);
Array.set(list, 0, fl);
System.out.println(Arrays.toString(list));
} catch (InstantiationException | IllegalAccessException |
IllegalArgumentException | InvocationTargetException
| NoSuchMethodException | SecurityException e) {
e.printStackTrace();
}
}
}
全メソッド取得
-
getMethodsメソッドでFileクラスのPublicメソッド列挙
- publicメソッドをMethodオブジェクトの配列として返す
- public以外も取りたいときはgetDeclaredMethodsメソッドで全メソッド取得
import java.io.File;
public class ReflectMethods {
public static void main(String[] args) {
//Fileクラス取得
var str = File.class;
//Fileクラス配下のpublicメソッドをgetNameでメソッド名列挙
for (var m : str.getMethods()) {
System.out.println(m.getName());
}
}
}
メソッド実行
- Methodオブジェクトを介して実行
- getMethodメソッドで個別のメソッドを取得し、invokeメソッドで実行
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectInvoke {
public static void main(String[] args) {
try {
//Fileクラス取得
var clazz = File.class;
//Fileオブジェクト生成
var f1 = clazz.getConstructor(String.class).newInstance("./data/data.txt");
var f2 = clazz.getConstructor(String.class).newInstance("./data/sample.txt");
//renameToメソッド取得、実行
Method m = clazz.getMethod("renameTo", File.class);
System.out.println(m.invoke(f1, f2));
} catch (NoSuchMethodException | SecurityException |
InstantiationException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
フィールド取得、設定
- publicフィールドはgetFieldメソッドで取得
- その他フィールドはgetDeclaredFiledメソッドで取得
- 引数は取得したいフィールドの名前を指定
- Fieldオブジェクト取得したらget/setメソッドでフィールド値を取得/設定
- privateフィールドはそのままアクセスできないので、setAccessibleメソッドでアクセスを明示的に許可
- 注意:リフレクションは低速
//PersonクラスのlastNameフィールド取得
import java.lang.reflect.InvocationTargetException;
import example.Person;
public class ReflectField {
public static void main(String[] args) {
try {
var clazz = Person.class;
var con = clazz.getConstructor(String.class, String.class);
var p = con.newInstance("太郎", "山田");
//フィールド取得
var last = clazz.getDeclaredField("lastName");
//privateフィールドへのアクセスを明示的に許可
last.setAccessible(true);
//get/setメソッドでフィールド値取得/設定
last.set(p, "鈴木");
System.out.println(last.get(p)); //鈴木
} catch (NoSuchMethodException | SecurityException |
InstantiationException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException |
NoSuchFieldException e) {
e.printStackTrace();
}
}
}
package example;
public class Person {
private String firstName;
private String lastName;
public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
@Override
public String toString() {
return String.format("名前は、%s %s です。",
this.lastName, this.firstName);
}
public String getLastName() {
return this.lastName;
}
public String getFirstName() {
return this.firstName;
}
}