Javaのリフレクション(Reflection)は、実行時にクラスやオブジェクトに関する情報を動的に取得・操作するための強力な機能です。これにより、コンパイル時には不明なクラスに対しても、そのフィールドやメソッドにアクセスしたり、インスタンスを生成したりできます。
Classオブジェクトの取得方法
リフレクションの中心となるのはjava.lang.Classクラスです。各クラスはJVM上でただ一つのClassオブジェクトを持ち、そのクラスの構造情報を保持しています。Classオブジェクトは以下の3つの方法で取得可能です。
ClassName.class:コンパイル時に型がわかっている場合object.getClass():既存のオブジェクトから取得Class.forName("完全修飾クラス名"):文字列から動的にロード
Class<?> clazz1 = String.class;
Class<?> clazz2 = "example".getClass();
Class<?> clazz3 = Class.forName("java.lang.String");
System.out.println(clazz1); // class java.lang.String
System.out.println(clazz2); // class java.lang.String
System.out.println(clazz3); // class java.lang.String
主要なリフレクションAPI
Classクラスには、フィールド、メソッド、コンストラクタ、継承関係などを取得するための多数のメソッドが用意されています。
| メソッド | 説明 |
|---|---|
getFields() | publicフィールド(継承含む)を取得 |
getDeclaredFields() | 宣言されたすべてのフィールド(アクセス修飾子問わず、継承除く) |
getMethods() | publicメソッド(継承含む)を取得 |
getDeclaredMethods() | 宣言されたすべてのメソッド(アクセス修飾子問わず、継承除く) |
getConstructors() | publicコンストラクタを取得 |
getDeclaredConstructors() | すべてのコンストラクタ(private含む)を取得 |
getSuperclass() | スーパークラスのClassオブジェクトを取得 |
getInterfaces() | 実装インターフェースを取得 |
メソッドの動的呼び出し
Methodクラスを使用すると、実行時に任意のメソッドを呼び出すことができます。プライベートメソッドの場合はsetAccessible(true)が必要です。
Class<?> studentClass = Class.forName("Reflection.Student");
Object instance = studentClass.getDeclaredConstructor().newInstance();
// publicメソッドの呼び出し
Method publicMethod = studentClass.getMethod("StudentPublicMethod", String.class);
String result1 = (String) publicMethod.invoke(instance, "hello");
System.out.println(result1); // hello
// privateメソッドの呼び出し
Method privateMethod = studentClass.getDeclaredMethod("StudentPrivateMethod", String.class);
privateMethod.setAccessible(true);
String result2 = (String) privateMethod.invoke(instance, "world");
System.out.println(result2); // world
フィールドへのアクセス
同様に、Fieldクラスを使えば、フィールドの値を読み書きできます。プライベートフィールドもsetAccessible(true)でアクセス可能になります。
Class<?> personClass = Class.forName("Reflection.Person");
Object person = personClass.getConstructor(String.class).newInstance("Alice");
Field nameField = personClass.getDeclaredField("Name");
nameField.setAccessible(true);
// 値の取得
String currentName = (String) nameField.get(person);
System.out.println(currentName); // Alice
// 値の変更
nameField.set(person, "Bob");
System.out.println(((Person) person).getName()); // Bob
コンストラクタの動的利用
コンストラクタもリフレクションで取得・実行可能です。プライベートコンストラクタも同様に扱えます。
Class<Student> clazz = Student.class;
// publicコンストラクタ
Constructor<Student> publicCtor = clazz.getConstructor(String.class, String.class);
Student s1 = publicCtor.newInstance("Tom", "S001");
// privateコンストラクタ
Constructor<Student> privateCtor = clazz.getDeclaredConstructor(String.class);
privateCtor.setAccessible(true);
Student s2 = privateCtor.newInstance("Jerry");
System.out.println(s1.getName()); // Tom
System.out.println(s2.getName()); // Jerry
アノテーションの取得
リフレクションはアノテーション情報の取得にも使用されます。getAnnotation()メソッドで特定のアノテーションを取得できます。
Class<Student> clazz = Student.class;
CustomDescriptions annos = clazz.getAnnotation(CustomDescriptions.class);
if (annos != null) {
for (CustomDescription desc : annos.value()) {
System.out.println("description: " + desc.description());
}
}
// 出力:
// description: 学生
// description: 人