こんにちは。「Javaを復習する初心者」です。
今回はリフレクションを使って、privateメソッドを呼び出すことをやりました。
メソッドのアクセス修飾子がprivateの場合、通常そのクラス内でのみ呼び出すことができます。ですが、リフレクションという機能を使うと他のクラスからも呼び出すことができます。
クラスを定義
例えば、以下のようにprivateメソッドを持つクラスを定義したとします。
Person
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引数以降が必要になります。