Javaのリフレクションとアノテーションについて

今日は勉強会の当番だったので、リフレクションとアノテーションについて話しました。
以下資料。

リフレクション

リフレクションとは

クラスのフィールド、メソッドなどの情報を取得したり、クラスを生成したり、メソッド名の文字列を使ってメソッドを呼び出したりすることができるAPIです。
Javaで自作フレームワークを作ろうと思ったら、リフレクションを使うと便利なので、ぜひ勉強しておきたい内容です。

以下のクラスをリフレクションで扱います。

public class Hoge{
    String name;
    public String getName(){
        return name;
    }
    public void setName(String name){
        this.name = name;
    }
}
Class

ClassはJavaのクラスの情報を得るためのクラスです。
以下のいずれかの方法でClassクラスは取得できます。

//方法1
Class c1 = Hoge.class;
//方法2
Hoge hoge = new Hoge();
Class c2 = hoge.getClass();
//方法3
Class c3 = Class.forName("Hoge");
インスタンスの生成(引数無しの場合)
Class c = Hoge.class;
Object hoge = c.newInstance();

で引数無しのデフォルトコンストラクタが実行され、Hogeインスタンスが生成されます。
これは、

Hoge hoge = new Hoge();

に相当します。

メソッドを実行
//setNameメソッドを抽出
Method m = c.getMethod("setName", new Class[]{String.class});
//setNameメソッドを実行
m.invoke(hoge, new Object[]{"ほげ"});
//getNameメソッドを抽出
m = c.getMethod("getName", new Class[0]);
//getNameメソッドを実行
System.out.println(m.invoke(hoge, new Object[0]));

これを実行すると、「ほげ」と表示されるはずです。

getMethodでClassからメソッドを取得します。
第一引数はメソッド名、第二引数はメソッドの引数のClassの配列を渡します。
Methodクラスのinvokeメソッドを実行することで、メソッドを実行することができます。
第一引数はそのメソッドを実行するオブジェクト。第二引数はそのメソッドに渡す引数で、Objectクラスの配列を渡します。
引数が必要ない場合、長さ0の配列を渡してやります。

フィールド(private)

fieldはgetFieldで取得できますが、今回はprivateなのでgetDeclaredFieldを使います。
値を取得したリセットしたりするには、setAccessible(true)を使う必要があります。

Field f = c.getDeclaredField("name");
f.setAccessible(true);
f.set(hoge, value);//フィールドに値をセット
Object value = f.get(hoge);//フィールドから値を得る

アノテーション

アノテーションとは

アノテーションはクラス、フィールド、メソッドなどにメタデータを付加するものです。

@Annotation
public class Hoge{
・・・
}

という形で利用されます。
Struts2やActiveObjectなどでも利用されており、今後も利用するフレームワークが増えていくことが予想されます。
Java5では@Overrideや@Deprecatedなどは標準であります(Java6は未調査)。
今回は自分でアノテーションを作って、リフレクションの機能でアノテーションから値を得ます。

メタアノテーション

アノテーションに付加するアノテーションがメタアノテーションです。

  • @Retention・・・アノテーション情報をどこまで保持するかを指定するアノテーションです。
  • @Target・・・クラスに付加するのかメソッドに付加するのかといったことを定義します。カンマ区切りで複数指定できます。
自作アノテーションの作成

以下はメソッドにつけるアノテーションです。
リフレクションで利用するのでRetentionPolicy.RUNTIMEを指定しています。

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target( { ElementType.METHOD })
public @interface AnnoTest{
	String value();
}

String value()はアノテーションに渡すことのできる値を定義したものです。

@AnnoTest(value="hoge")

というように使います。
valueは特殊な名前で、これ一つしかない場合は、

@AnnoTest("hoge")

という形で付加することができます。

アノテーションをリフレクションで取得

以下のようにHogeクラスを変更します。

@AnnoTest("test")
public void getName(){・・・}

以下のコードでアノテーションを取得して表示します。

AnnoTest a = Hoge.class.getMethod("getName", new Class[0]).getAnnotation(AnnoTest.class);
//アノテーションにセットされた値の取得
System.out.println(a.value());

普通にJavaプログラミングを書いている時には、自分でリフレクションやアノテーションを作ったり使ったりする機会はあまりなさそうですが、知っていると使える時が来るかもしれないので一度勉強しておきたいですね。