こんにちは。ハンドルネーム「Javaを復習する初心者」です。このサイトはプログラミング言語Javaの復習・学習をするブログです。プログラムの開発・実行はEclipseで行ってます。
スポンサーリンク
お知らせ
  • 参考文献のページ作りました。
  • Amazon.co.jpアソシエイトに参加していますが、参考文献の紹介はもしもアフィリエイトに統一しました。
  • 2016年10月9日からは投稿ペースを落とします。週1回くらいにする予定です。
スポンサーリンク

リフレクションでprivateメソッドを呼び出す

こんにちは。「Javaを復習する初心者」です。今回はリフレクションを使って、privateメソッドを呼び出すことをやりました。

メソッドのアクセス修飾子がprivateの場合、通常そのクラス内でのみ呼び出すことができます。ですが、リフレクションという機能を使うと他のクラスからも呼び出すことができます。

クラスを定義

例えば、以下のようにprivateメソッドを持つクラスを定義したとします。

public class Person {

    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");

    private String name;
    private String age;
    private String time;

    public Person(String name, String age) {
        if (!checkAge(age)) {
            throw new IllegalArgumentException("ageが不正です: " + age);
        }
        this.name = name;
        this.age = age;
        this.time = criateTime();
    }

    private boolean checkAge(String age) {
        return age.matches("\\d+");
    }

    private String criateTime() {
        return LocalDateTime.now().format(FORMATTER);
    }

    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + ", time=" + time + "]";
    }

}

上記のcheckAge()メソッド、criateTime()メソッドがprivateです。これをリフレクションを使って呼び出してみました。

引数を持たないメソッド呼び出し

最初に、引数を持たない方のcriateTime()メソッドを呼び出してみました。以下のようにリフレクションを使います。

        Person person = new Person("name1", "21");
        Method method = null;
        try {
            method = Person.class.getDeclaredMethod("criateTime");
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        }

        method.setAccessible(true);

        Object object = null;
        try {
            object = method.invoke(person);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

        System.out.println(object);
20161001171627

Method型変数を定義してインスタンスを格納します。ここの手順はフィールドのときと同じような流れです。getDeclaredMethod()メソッドの第1引数にメソッド名をしています。呼び出すメソッドに引数がない場合、getDeclaredMethod()メソッドの第1引数のみ指定します。ここで、メソッド名が見つからない場合はNoSuchMethodExceptionがスローされます。Eclipseの機能でcatch節でSecurityExceptionをキャッチするように自動補完されたのですが、この例外が発生する条件は分かりませんでした。

次に「method.setAccessible(true);」を実行します。privateメソッドを呼び出す場合にはこの設定が必要です。

invoke()メソッドで実際にメソッドを呼び出すことができます。引数にはインスタンスを指定していますが、これは呼び出すメソッドがstaticではないからです。呼び出すメソッドに引数がない場合は、invoke()メソッドの第1引数のみ指定します。結果はObject型ですので、適宜キャストする必要がありますが、今回はprintlnメソッドの引数に指定するだけにしたので、キャストはしていません。

上記が引数がないメソッドの呼び出しの場合です。

引数を持つメソッド呼び出し

次にcheckAge()メソッドを呼び出しました。以下がソースです。

        Person person = new Person("name1", "21");
        Method method = null;
        try {
            method = Person.class.getDeclaredMethod("checkAge", String.class);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        }

        method.setAccessible(true);

        Object object = null;
        try {
            object = method.invoke(person, "abc");
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

        System.out.println(object);
false

呼び出すメソッドに引数がある場合は、上記のようにgetDeclaredMethod()メソッド、invoke()メソッドともに第2引数以降が必要になります。