モチベーション
Javaにはオブジェクトの型情報を取得するためにリフレクションという機能が用意されています。いままで名前と概要は知っていたのですが、きちんと知っておきたいと考えたのでまとめます。
リフレクションとは
Javaでは実行時に動的に型の情報を取得し、操作することができます。
そのように、プログラムの実行過程でプログラム自身の構造を読み取ったり、書き換えたりする技術のことを情報工学ではリフレクションと呼びます。
Javaのリフレクションではおもに以下のような機能を提供します。
- クラスやインターフェースの型や名前の取得
- フィールドの値の読み書きメソッドの読み出し
- プロキシクラスによる処理の差し込み
フレーワークやなどではこのリフレクションを利用してフィールドへのアクセスやメソッドの呼び出しを可能としています。
Javaリフレクションの仕組み
Javaのリフレクションの仕組みは以下の2つのクラスと機能を活用することで可能となります。
java.lang.Class
- クラスローダー
java.lang.Class
Javaにはクラスを表すjava.lang.Class
というクラスが用意されています。ソースコード内で扱うクラスやインターフェースにはそれぞれ対応するClassオブジェクトが存在します。これは、例えばStringであればClass<String>
のように表現されます。
このクラスは特殊でnew
などを使ってインスタンス化ができません。代わりにクラスをロードする際にJVMによって生成されるか、クラスローダーのdefineClassメソッドの呼び出しによって自動的に構築されます。
Javaでは生成されたClassオブジェクトから型情報を取得することが可能となっています。
クラスローダー
クラスローダーとは文字通りクラスをロードする役割をもつオブジェクトになります。
ソースコードからClassオブジェクトを取得する際は内部的にこのクラスローダーから取得することになります。
リフレクションを活用してクラス情報にアクセスしてみる。
せっかくなんで簡単にですがリフレクションを使ってみたいと思います。
まずは以下のような簡単なクラスを用意します。
public class Foo {
Foo(String name){
this.name = name;
}
private String name;
public void greet(){
System.out.println("Hello, " + name);
}
}
メソッドを呼び出してみる。
上記のFoo.class
クラスに対してリフレクションを利用してFoo#greet()
を呼び出してみます。
import java.lang.reflect.Method;
public class ReflectionSample {
public static void main(String[] args) {
Class<Foo> fooClazz1 = Foo.class;
Foo foo = new Foo("taro");
try {
Method greetMethod = fooClazz1.getMethod("greet");
greetMethod.invoke(foo);
} catch (ReflectiveOperationException e) {
e.printStackTrace();
}
}
}
あるクラスのメソッド呼び出しはjava.lang.reflect.Method#invoke
を使って行います。
Method
クラスはClass#getMethod
から取得することができます。その際の引数には呼び出したいメソッド名を指定します。
今回のFoo#greet()
は引数なしですが、第二引数以降に、呼び出すメソッドの引数を渡すことができます。
実行結果
Hello, taro
感想
普段、業務アプリ書くときに使うことは少ないかもしれませんが、フレームワークのコードリーディングする際にはこの辺の知識がいるのではないかと思いました。