リフレクションとは?
リフレクションは、ヒープ領域にロードされたClass型のオブジェクトを通じて、目的のクラスのインスタンスを作成できるようにサポートし、インスタンスのフィールドとメソッドにアクセス修飾子に関係なくアクセスできるようにするAPIです。
ここでロードされたクラスとは、JVMのクラスローダーでクラスファイルのロードが完了した後、そのクラスの情報を保持したClass型のオブジェクトを生成してメモリのヒープ領域に保存したことを意味します。newキーワードで作成するオブジェクトとは異なることに注意してください。もし、このClass型のオブジェクトについて理解が不足している場合は、java.lang.classオブジェクトのJDKドキュメントを確認すると良いでしょう。
使用方法
リフレクションを使用する前に、ヒープ領域にロードされたClass型のオブジェクトを取得する必要があります。3つの方法があります。
- クラス.classで取得する
- インスタンス.getClass()で取得する
- Class.forName("クラス名")で取得する
3つの方法で取得したClass型のインスタンスは、すべて同じであることを確認できます。どの方法を使用してもハッシュ値が同じなので、状況に応じて適切に使用すればよいでしょう。
これで、取得したClass型を通じて、そのクラスのインスタンスを作成したり、インスタンスのフィールドとメソッドにアクセス修飾子に関係なくアクセスできるようになりました。まずは、そのクラスのインスタンスを作成してみましょう。
getConstructor()を使用してコンストラクタを取得し、newInstance()を使用してMemberインスタンスを動的に生成することができます。
最後に、インスタンスのフィールドとメソッドにアクセス修飾子に関係なくアクセスして使用してみましょう。
getDeclaredFileds()を使用して、クラスのインスタンス変数をすべて取得し、get()を使用してフィールド値を返し、set()を使用してフィールド値を変更できることがわかります。ここで注意すべき点は、privateアクセス修飾子を持つフィールドにアクセスする場合は、setAccessible()の引数をtrueにする必要があることです。
メソッドもgetDeclaredMethod()を使用して取得できます。このとき、メソッドの名前とパラメータの型を一緒に引数として渡す必要があります。同様に、privateアクセス修飾子を持つメソッドにアクセスする場合は、setAccessible()の引数をtrueに設定する必要があります。最後に、invoke()メソッドを使用して、リフレクションAPIで取得したメソッドを呼び出すことができます。
長所と短所
- 長所
- 実行時にクラスのインスタンスを作成し、アクセス修飾子に関係なくフィールドとメソッドにアクセスして、必要なタスクを実行できる柔軟性があります。
- 短所
- カプセル化を損なう。
- 実行時にインスタンスを作成するため、コンパイル時にその型をチェックできません。
- 実行時にインスタンスを作成するため、具体的な動作の流れを把握しづらい。
- 単にフィールドやメソッドにアクセスする場合よりも、リフレクションを使用してアクセスする場合のパフォーマンスが低下します。(すべての状況でパフォーマンスが低下するわけではありません。)
使用する理由
リフレクションAPIを使用すると、実行中にクラス情報にアクセスして、クラスを自由に操作できます。privateアクセス修飾子で宣言されたフィールドやメソッドまで操作可能です。オブジェクト指向設計において重要なカプセル化が損なわれるため、使用すべきではない技術のように見えます。
小規模なコンソール段階では、開発者はコンパイル時にプログラムで使用されるオブジェクトと依存関係をすべて把握できます。しかし、フレームワークなど大規模な開発段階では、無数のオブジェクトと依存関係を把握するのは困難です。このとき、リフレクションを使用すると、動的にクラスを作成して依存関係を結び付けることができます。
例えば、SpringのBeanFactoryを見ると、@Controller、@Service、@Repositoryなどのアノテーションを付けるだけで、BeanFactoryが自動的にそのアノテーションが付いたクラスを作成して管理していることがわかります。開発者はBeanFactoryにそのクラスを教えたことがないのに、なぜこれが可能なのかというと、それはリフレクションのおかげです。実行時にそのアノテーションが付いたクラスを検索して発見すると、リフレクションを使用してそのクラスのインスタンスを作成し、必要なフィールドを注入してBeanFactoryに保存する形で使用されます。
もちろん、前述したようにカプセル化を損なうため、本当に必要な場合にのみ使用するようにしてください。
コメント0