Javaリフレクション(Reflection)

Javaリフレクション(Reflection)は強力な機能であり、プログラムが実行時にクラス、インターフェース、フィールド、メソッドの情報を照会・アクセス・変更することを可能にします。リフレクションはクラスを動的に操作する能力を提供し、これは多くのフレームワークやライブラリで広く使用されています。例えば、Springフレームワークの依存性注入です。

リフレクションAPI

JavaのリフレクションAPIは、Classオブジェクトを操作するための一連のクラスとインターフェースを提供します。主なクラスは以下のとおりです:

  • java.lang.Class:クラスのオブジェクトを表します。クラスのフィールド、メソッド、コンストラクタなどを取得するメソッドを提供します。
  • java.lang.reflect.Field:クラスのフィールド(属性)を表します。フィールドへのアクセスと変更の機能を提供します。
  • java.lang.reflect.Method:クラスのメソッドを表します。メソッドを呼び出す機能を提供します。
  • java.lang.reflect.Constructor:クラスのコンストラクタを表します。オブジェクトを作成する機能を提供します。

処理フロー

  1. 取得Classオブジェクト:まずターゲットクラスのClassオブジェクトを取得します。
  2. メンバー情報の取得:...を介してClassオブジェクトを介して、クラスのフィールド、メソッド、コンストラクタなどの情報を取得できます。
  3. メンバーの操作:リフレクションAPIを使用すると、フィールドの値の読み取りと変更、メソッドの呼び出し、オブジェクトの作成ができます。

以下は、Javaリフレクションの基本的な使用方法とその一般的な応用例です。

1. Classオブジェクトの取得

すべてのクラスはJVM内に対応するClassオブジェクトを持っています。以下の方法でClassオブジェクトを取得できます:

クラスリテラルを使用する

Class<?> clazz = String.class;

オブジェクトインスタンスを使用する:

String str = "Hello";
Class<?> clazz = str.getClass();

Class.forName() メソッドを使用する:

Class<?> clazz = Class.forName("java.lang.String");

2. オブジェクトの作成

リフレクションを使用してオブジェクトを動的に作成できます:

Class<?> clazz = Class.forName("java.lang.String");
Object obj = clazz.getDeclaredConstructor().newInstance();

3. フィールドへのアクセス

リフレクションを使用して、クラスのフィールドにアクセス・変更できます:

Class<?> clazz = Person.class;
Field field = clazz.getDeclaredField("name");
field.setAccessible(true); // 如果字段是私有的,需要设置为可访问
Object value = field.get(personInstance); // 获取字段值
field.set(personInstance, "New Name"); // 设置字段值

4. メソッドの呼び出し

リフレクションを使用してクラスのメソッドを呼び出すことができます:

Class<?> clazz = Person.class;
Method method = clazz.getMethod("sayHello");
method.invoke(personInstance);

Method methodWithArgs = clazz.getMethod("greet", String.class);
methodWithArgs.invoke(personInstance, "World");

5. コンストラクタの取得

リフレクションを使用してコンストラクタを取得・呼び出しできます:

Class<?> clazz = Person.class;
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
Object obj = constructor.newInstance("John", 30);

6. インターフェースとスーパークラスの取得

リフレクションを使用して、クラスが実装するインターフェースとスーパークラスを取得できます:

Class<?> clazz = Person.class;

// 获取所有接口
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> i : interfaces) {
    System.out.println("Interface: " + i.getName());
}

// 获取父类
Class<?> superClass = clazz.getSuperclass();
System.out.println("Superclass: " + superClass.getName());

以下は、リフレクションを使用してオブジェクトの作成、フィールドへのアクセス、メソッドの呼び出しを行う方法を示す完全な例です:

例

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class ReflectionExample {

    public static void main(String[] args) throws Exception {
        // Classオブジェクトの取得
        Class<?> clazz = Person.class;
       
        // オブジェクトの作成
        Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
        Object person = constructor.newInstance("John", 30);
       
        // フィールドへのアクセス
        Field nameField = clazz.getDeclaredField("name");
        nameField.setAccessible(true);
        System.out.println("Name: " + nameField.get(person));
       
        // フィールドの変更
        nameField.set(person, "Doe");
        System.out.println("Updated Name: " + nameField.get(person));
       
        // メソッドの呼び出し
        Method greetMethod = clazz.getMethod("greet", String.class);
        greetMethod.invoke(person, "World");
    }
}

class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void greet(String message) {
        System.out.println(name + " says: " + message);
    }
}

上記のコードをコンパイルして実行すると、出力結果は次のようになります:

Name: John
Updated Name: Doe
Doe says: World

java.lang.reflect

java.lang.reflectはJavaリフレクション機構のコアパッケージであり、クラスとそのメンバー(フィールド、メソッド、コンストラクタなど)を操作するためのクラスとインターフェースを提供します。これらのAPIを通じて、開発者は実行時にクラスの構造を動的に照会・変更できます。

以下はjava.lang.reflectパッケージ内の主要なクラスとインターフェースの詳細な説明です:

1. Class 类

  • 機能:クラスのオブジェクトを表し、フィールド、メソッド、コンストラクタなどのクラス情報を取得するメソッドを提供します。
  • 主なメソッド:
    • getFields():すべてのpublicフィールドを取得します。
    • getDeclaredFields():privateフィールドを含む、宣言されたすべてのフィールドを取得します。
    • getMethods():すべてのpublicメソッドを取得します。
    • getDeclaredMethods():privateメソッドを含む、宣言されたすべてのメソッドを取得します。
    • getConstructors():すべてのpublicコンストラクタを取得します。
    • getDeclaredConstructors():privateコンストラクタを含む、宣言されたすべてのコンストラクタを取得します。
    • getSuperclass():クラスのスーパークラスを取得します。
    • getInterfaces():クラスが実装するすべてのインターフェースを取得します。

2. Field 类

  • 機能:クラスのフィールド(属性)を表し、フィールド値へのアクセスと変更を行うメソッドを提供します。
  • 主なメソッド:
    • get(Object obj):指定されたオブジェクトのフィールド値を取得します。
    • set(Object obj, Object value):指定されたオブジェクトのフィールド値を設定します。
    • getType():フィールドのデータ型を取得します。
    • getModifiers():フィールドの修飾子(public、privateなど)を取得します。

3. Method 类

  • 機能:クラスのメソッドを表し、メソッドを呼び出す機能を提供します。
  • 主なメソッド:
    • invoke(Object obj, Object... args):指定されたオブジェクトのメソッドを呼び出します。
    • getReturnType():メソッドの戻り値の型を取得します。
    • getParameterTypes():メソッドのパラメータの型を取得します。
    • getModifiers():メソッドの修飾子(public、privateなど)を取得します。

4. Constructor 类

  • 機能:クラスのコンストラクタを表し、オブジェクトを作成する機能を提供します。
  • 主なメソッド:
    • newInstance(Object... initargs):指定されたコンストラクタパラメータを使用して、新しいインスタンスを作成します。
    • getParameterTypes():コンストラクタのパラメータの型を取得します。
    • getModifiers():コンストラクタの修飾子(public、privateなど)を取得します。

サンプルコード

以下は...を使用したjava.lang.reflectパッケージによるリフレクション操作の例です:

例

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Constructor;

public class ReflectionExample {

    public static void main(String[] args) throws Exception {
        // Classオブジェクトの取得
        Class<?> clazz = Car.class;

        // Carオブジェクトの作成
        Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
        Object car = constructor.newInstance("Toyota", 2020);

        // フィールドへのアクセスと変更
        Field modelField = clazz.getDeclaredField("model");
        Field yearField = clazz.getDeclaredField("year");
       
        // フィールドをアクセス可能に設定(フィールドがprivateの場合)
        modelField.setAccessible(true);
        yearField.setAccessible(true);
       
        // 元のフィールド値を出力
        System.out.println("Original Model: " + modelField.get(car));
        System.out.println("Original Year: " + yearField.get(car));
       
        // フィールド値を変更
        modelField.set(car, "Honda");
        yearField.set(car, 2024);
       
        // 変更後のフィールド値を出力
        System.out.println("Updated Model: " + modelField.get(car));
        System.out.println("Updated Year: " + yearField.get(car));
       
        // メソッドの呼び出し
        Method startMethod = clazz.getMethod("start");
        startMethod.invoke(car);
    }
}

class Car {
    private String model;
    private int year;

    public Car(String model, int year) {
        this.model = model;
        this.year = year;
    }

    public void start() {
        System.out.println("The " + model + " car of year " + year + " is starting.");
    }
}

上記のコードをコンパイルして実行すると、出力結果は次のようになります:

Original Model: Toyota
Original Year: 2020
Updated Model: Honda
Updated Year: 2024
The Honda car of year 2024 is starting.
その他の拡張