Edited at

何かの時にスッと使える力技 - Reflection 編

More than 1 year has passed since last update.


リフレクションとは

リフレクションとは Java の標準ライブラリに含まれている、クラスのメタ情報を取り扱うための API 群で、java.lang.reflectパッケージにまとめられています。

リフレクションを使うことで、クラスに定義されているコンストラクタやメソッド、フィールドの一覧を読み取ったり、そこからメソッドを呼び出したりフィールドの値を取り出したりということができます。通常であれば、リフレクションを使わずとも直接インスタンスを作ってメソッドを呼び出す手続きで事足りますが、リフレクションでは不可視なメソッドにアクセスしたり、finalなフィールドを変更したりというチート技ができるようになります。


リフレクションでできること

すべての型には、.classというリテラルがあります。String.classとすると、Stringクラスに関する情報を持ったClassクラスが得られます。

Classクラスには、目的のクラスにどんなメソッドが定義されているか検索するメソッドや、定義されているフィールドの情報を取り出すメソッドなどがあります。この他、インスタンスメソッドとしてgetClass()がすべてのクラスに定義されていて、このメソッドを使っても同じくClassクラスが得られます。 

例えば、以下のようにすると、privateで定義された本来見えないはずのメソッドが呼び出せるようになります。

// リフレクションでアクセスするクラス。

public class Something {
private String hoge() {
return "hoge";
}
}

public class Main {
public static void main(String[] args) {
Something target = new Something();

try {
Class<Something> clazz = target.getClass(); // class は予約語なので慣例的に clazz や klass などにする
Method method = clazz.getDeclaredMethod("hoge", null); // 第二引数は呼び出すメソッドの引数の型の配列。無いときは null にする
method.setAccessible(true); // 見えないはずのメソッドを見えるようにする魔法のメソッド
String result = (String) method.invoke(target, null); // 第一引数にメソッドを呼び出すインスタンス、第二引数以後は実際の引数
System.out.println(result); // "hoge" が出力される
} catch (NoSuchMethodException e) {
// 文字列で指定したメソッド名に一致するメソッドが無いとき
} catch (InvocationTargetException e) {
// 呼び出したメソッドの中で例外がおきたとき
} catch (IllegalAccessException e) {
// アクセスできないメソッドにアクセスしたとき
}
}
}


リフレクションで気をつけること

リフレクションをつかったプログラムを動かすとき、VM によってはリフレクションによるアクセスに厳しいルールを設定しているものもあります。その場合、setAccessible(true)を呼び出すとSecurityExceptionを投げます。

この他、リフレクションではメソッド、クラス、フィールドすべて名前を文字列で指定するため、その文字列に一致するものがなければ例外になります。よって、ProGuard 等でコードの難読化をするときはリフレクションを用いたコードに気をつける必要があります(フィールド名やメソッド名が変えられても、文字列までは変えてくれないので)。

また、通常のメソッド呼び出しと異なりリフレクション API を経由するため、どうしてもパフォーマンスに影響が出ます。

言うまでもありませんが、普通ではアクセスできないものに無理やりアクセスできるようになったり、データを書き換えたりといった事ができるので、用法用量を守って正しくお使いください。


ところで、決まりきったコード多くないですか

まとめて置いておきますね。

https://github.com/Drivemode/Intenso