スライドショー

【Java入門】リフレクションでメソッドの実行、フィールドの変更

リフレクションはクラスやメソッドを変数として扱うことができて、異なるクラスやメソッドを1つのプログラムで実行できるので便利です。

プログラムをテストする時などでよく使われたりします。

この記事ではリフレクションについて、

  • リフレクションとは
  • リフレクションの使い方
  • クラスの取得
  • インスタンスの生成
  • メソッドの取得と実行
  • privateフィールドの参照と変更
  • リフレクションの危険性について

など、リフレクションの基礎から具体的な使い方についても解説していきます。

リフレクションとは

リフレクションとは、クラス名やメソッド名を指定して「クラスを動的に実行する」ことです。

「クラスを動的に実行する」とはクラス名やメソッド名を文字列の変数として指定することで、クラス名やメソッド名を変更してもプログラムが実行できるようにしています。

クラス名やメソッド名を変数として変更してもプログラムが実行できるので、プログラムのテストなどで使用されますちなみに、コマンドラインでJavaのプログラムを実行する際に

java クラス名

と記述しますが、これもクラス名を変更してプログラムが実行できるのでリフレクションを行っていることになります。

リフレクションの使い方

リフレクションの使い方について、クラス、インスタンス、メソッド、フィールドの順にみていきましょう。

クラスの取得

クラスを取得するためにはClassクラスを使用しますClassクラスの使い方は以下のようにいくつか方法があります。

// 方法1
Class<クラス名> オブジェクト名 = クラス名.class;

// 方法2
クラス名 オブジェクト名1 = new クラス名();
Class<? extends クラス名> オブジェクト名2 = オブジェクト名1.getClass();

// 方法3
Class<?> オブジェクト名 = Class.forName("クラス名");

foNameメソッドを使用する場合は、ClassNotFoundExceptionの例外処理を記述する必要があります。

なお、リフレクションに関連する例外の共通クラスReflectiveOperationExceptionで処理することも可能です。

インスタンスの生成

インスタンスを生成する記述は以下のとおりになります。

Class<クラス名> オブジェクト名1 = クラス名.class;
Object オブジェクト名2 = オブジェクト名1.newInstance();

これは、

クラス名 オブジェクト名2 = new クラス名();

に相当します。

メソッドの取得と実行

メソッドを取得するにはMethodクラスのオブジェクトにgetMethodメソッドの戻り値を格納します。

Method オブジェクト名 = Classクラスのオブジェクト名.getMethod(メソッド名, 引数1の型名.class, 引数2の型名.class, ・・・);

getMethodメソッドの第2引数以降はClassクラスの可変長引数になります。

メソッドを実行するためにはinvokeメソッドを実行します。

Methodクラスのオブジェクト.invoke(生成したインスタンス, 引数1, 引数2, ・・・)

invokeメソッドの第2引数以降は可変長引数になります。

サンプルコードで確認しましょう。

import java.lang.reflect.Method;

class TestClass {
    private String str;
    
    public void setStr(String str) {
        this.str = str;
    }
    
    public String getStr() {
        return str;
    }
} 

public class Main {
 
    public static void main(String[] args) {
        // クラス、メソッドを文字列で指定
        String strClass = "TestClass";
        String strMethod1 = "setStr";
        String strMethod2 = "getStr";
        
        // テスト用の変数
        String str1 = "test";
        
        // リフレクション
        try {
            // クラスの取得
            Class<?> c = Class.forName(strClass);
            // インスタンスの生成
            Object myObj = c.newInstance();
            
            // メソッド(setStr)の取得
            Method m = c.getMethod(strMethod1, String.class);
            // メソッド(setStr)の実行
            m.invoke(myObj, str1);
            
            // メソッド(getStr)の取得
            m = c.getMethod(strMethod2);
            // メソッド(getStr)の実行
            System.out.println(m.invoke(myObj)); 
        } catch(ReflectiveOperationException e) {
            e.printStackTrace();
        }
    }

}

実行結果:

test

このサンプルコードでは、クラス名とメソッド名をそれぞれString型の変数に格納しています。

クラス名やメソッド名を格納した変数をforNameメソッドgetMethodメソッドinvokeメソッドなどのメソッドの引数に指定して実行しています。

privateフィールドの参照と変更

ここではprivateフィールドの参照と変更の方法について見ていきましょう以下のサンプルコードで確認しましょう。

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

class TestClass {
    private String str;
    
    public void setStr(String str) {
        this.str = str;
    }
    
    public String getStr() {
        return str;
    }
} 

public class Main {
 
    public static void main(String[] args) {
        // クラス、メソッド、フィールドを文字列で指定
        String strClass = "TestClass";
        String strMethod1 = "setStr";
        String strMethod2 = "getStr";
        String strField = "str";
        
        // テスト用の変数
        String str1 = "test";
        String str2 = "テスト";
        
        // リフレクション
        try {
            // クラスの取得
            Class<?> c = Class.forName(strClass);
            // インスタンスの生成
            Object myObj = c.newInstance();
            
            // メソッド(setStr)の取得
            Method m = c.getMethod(strMethod1, String.class);
            // メソッド(setStr)の実行
            m.invoke(myObj, str1);
            
            // フィールド(str)の取得
            Field f = c.getDeclaredField(strField);
            f.setAccessible(true);
            System.out.println(f.get(myObj)); 
            
            // フィールド(str)の変更
            f.set(myObj, str2);
            System.out.println(f.get(myObj));
        } catch(ReflectiveOperationException e) {
            e.printStackTrace();
        }
    }

}

実行結果:

test
テスト

このサンプルコードでは、まずstrMethod1で指定されたメソッドでフィールドに値を書き込んでいます。

その値をFieldクラスのオブジェクトfから呼び出されたgetメソッドを使って取得しています。また、その値をオブジェクトfから呼び出されたsetメソッドを使って変更しています。

リフレクションの危険性について

リフレクションを使用する場合は以下のことに注意しましょう。

クラス設計を破壊する危険性

これまでお伝えしたように、アクセス修飾子privateで「カプセル化」した値を、直接取得・変更することができました。

このように直接操作することを可能にしてしまうため、リフレクションには既存のクラス設計を破壊してしまう危険性があります。

コンパイル時にエラーの検出対象外

以前はリフレクションを使用した部分のコードは、コンパイル時のエラー検出対象外でエラーを検出させることができませんでした。

リフレクション部分のエラーはプログラム実行時にのみ検出するしかありませんでした。

しかし、Java7以降ではReflectiveOperationExceptionでリフレクションに関連する例外の共通クラスが定義されたため、例外処理を記述できるようになりました。

まとめ

ここでは、リフレクションについて説明しました。

リフレクションを使って記述すれば、後はクラス名やメソッド名など変数に代入する値を変更するだけで異なるクラスやメソッドを実行することができます。プログラムをテストする場合などで使用すると便利です。

使いこなすことができるように、この記事を何度も参考にして下さいね!

LINEで送る
Pocket

無料でSEからWebエンジニアへ転職しませんか?



侍エンジニア塾では、完全未経験の方から現在SEだけどプログラミングはやっていないという経験者まで、幅広い方々の人生を好転させるプログラミング指導を行ってきました。SEの方とお話していくなかで、

  • システムエンジニアという職業だけどコードが書けない
  • 事務作業が多くスキルがないため将来が不安
  • スクールに通うと完全未経験者と同じスタートになるからレベルが合わない
という、すでに知識があるSEならではのお悩みがあることに気づきました。そんな方におすすめなのが、弊社の「転職コース 」です。

弊社では、マンツーマンでレッスンを行いますので、現在お持ちの知識レベルからカリキュラムを作成いたします。さらにこちらの転職コースは無料で受講を始められて転職成功でそのまま卒業できるというとてもお得なコースとなっています。

既に知識のあるSEといっても転職は年齢が若いほど受かりやすいため、まずは無料体験レッスンで今の現状や理想の働き方について一緒に考えていきましょう。

まずは無料体験レッスンを予約する

書いた人

長野 透

長野 透

熊本在住のフリープログラマ兼ライターです。C/C++/C#、Java、Python、HTML/CSS、PHPを使ってプログラミングをしています。専門は画像処理で最近は機械学習、ディープラーニングにはまっています。幅広くやってきた経験を活かしてポイントをわかりやすくお伝えしようと思います。
お問合せはこちらでも受け付けています。
[email protected]

おすすめコンテンツ

あなたにぴったりなプログラミング学習プランを無料で診断!

プログラミング学習の効率を劇的に上げる学習メソッドを解説