2008年6月13日金曜日

Javaでprivateなメンバーにアクセスする方法

前回はprivate変数へのアクセスを簡単に解説したんだけど、みんなやっぱりその辺気になるみたいで結構アクセスがあるので二匹目のドジョウを狙ってもう少し詳しく調べてみた。
前回の記事はこちら
Javaでprivateなインスタンス変数にアクセスする方法

陽気な外人の翻訳風に書いてみる。

AccessibleObject

まずAccessibleObjectクラスについてみてみよう。
AccessibleObjectクラスは前回触れたFieldクラスの抽象クラスで(親クラスともいうね。ここで抽象クラスって言ったのは複数のサブクラスが存在していてそれらを使う場合にこの型を使って抽象的に書けることを強調したいからなんだ。)、アクセス権に関するメソッド群が定義されている。
ドキュメントにはこう書かれている。(これを探すのに何分も掛かった。Sunはドキュメントを見せない事で議論を活発にさせJavaを普及させようとしてるんだlol)



AccessibleObject クラスは、Field オブジェクト、Method オブジェクト、および Constructor オブジェクトの基底クラスです。このクラスを使うと、リフレクトされたオブジェクトの使用時に、デフォルトの Java 言語アクセス制御チェックを抑制するかどうかのフラグ設定を行えます。アクセスチェックは、次の場合に public、デフォルト (package) アクセス、protected、および private メンバーに対して実行されます。 つまり、フィールドの設定または取得に Field が使用される場合、メソッドの呼び出しに Method が使用される場合、あるいはクラスの新しいインスタンスの生成および初期化に Constructor が使用される場合です。



リフレクトされたオブジェクトで accessible フラグを設定すると、十分な特権を持つ高度なアプリケーション (Java のオブジェクトの直列化やその他の持続性機構など) は、通常は禁止されている方法でオブジェクトを操作できます。



AccessibleObject (Java Platform SE 6) から引用

publicでないメソッドにアクセスする際のチェックを抑制する事で目的のメンバー(変数、メソッド、内部クラス等クラス内に定義されている物なんかをメンバーと呼ぶんだ。クラスは一つのチームだと考えるといいかもね、いろんな奴が集まってるからprivateなシャイな奴もいるってわけ。もちろん冗談だけどねlol)へのアクセスをできる。
そして、AccessibleObjectにはField,Method,Constructorの三つのサブクラスがあり、それぞれがフィールド変数(インスタンス変数やクラス変数などクラスが直接持っている変数。それに対しメソッド内に定義された物がローカル変数。ローカル変数は本当に恥ずかしがりやだから話しかける(アクセスする)事は本当に難しいかも)、メソッド、コンストラクタに対応している。
前回はインスタンス変数にアクセスするためにFieldクラスを使用したけど、ほかのメンバーにアクセスしたい時はそれに対応したクラスを使うことでできるようになるんだ。(そう、女の子に合わせて口説き型を変えるみたいな感じかなlol)
じゃ~今回はこれを利用してprivateなコンストラクタが定義されたクラスをインスタンス化する方法を紹介しよう。


シャイなあの子のハート(コンストラクタ)をこじ開けろ!!

privateなコンストラクタを持ったクラスをインスタンス化できないのは何故だろう?
それはprivateは自クラスからしかアクセスできない事を表す修飾子でそれがコンストラクタに付いてるって事は、インスタンス化する際に初期化ができないからなんだ。
じゃ~なんでそんな事ができるかって?
それはstaticなメソッドが定義されたユーティリティークラスでインスタンス化しないで使えるクラスや、シングルトンパターンを適応したクラスなんかで使われるからさ。

// シングルトンパターン
public class Singleton {
private static Singleton singleton;
// static変数でこのクラスの型

private Singleton() {}
// privateなコンストラクタ

public static Singleton newInstance() {
// インスタンス化をするpublicなstaticメソッド
// このメソッド内でインスタンス化するのでprivateコンストラクタであっても
// インスタンス化することができる

if (singleton == null) {
// null チェックをしてまだインスタンス化していないかを確認して
// インスタンス化していなければnewする
singleton = new Singleton();
}
return singleton;
}
}


だから普通はprivateなコンストラクタを持っていたとしてもそれは必要だからそうしているのであって、無理に変更してインスタンス化するなんて事はあまり無いんだけど、何事も経験だから(経験無しにいきなり本番じゃ~焦るのも無理ないからね)今回はチャレンジしてみよう。

public class PrivateConstructor {

  // privateなコンストラクタ
private PrivateConstructor() {
System.out.println("え、やだっそんな無理やりインスタンス化なんて・・・らめぇええええ");
}

public void helloObject() {
System.out.println("ちょ、ちょっと!!なに勝手にインスタンス化してんのよ!!");
System.out.println("・・・別にアンタのためにインスタンス化したわけじゃないんだからねっ!///");
}
}


これがprivateなコンストラクタを持ったクラス。なんか書いてあるけど気にしないでコンストラクタがprivateになっているのを確認してほしい。
そしてこれを呼び出す側のコード。

public class Main {
public static void main(String[] args) throws Exception {
// PrivateConstructor pConstructor = new PrivateConstructor();
// コンストラクタをprivateにするとインスタンス化できるのはそのクラスだけなので
// このコードはコンパイルエラーとなる

Constructor constructor =
PrivateConstructor.class.getDeclaredConstructor(new Class[]{});
// まず前回同様にClass#getDeclared~メソッドを使ってprivateなメンバーを取得する
// 引数にはコンストラクタで指定されている引数の型のクラスを渡す
// 今回の用に無い場合は空の配列でおk

constructor.setAccessible(true);
// Accessibleフラグをtrueにしアクセス権チェックを抑制する

PrivateConstructor pConstractor = constructor.newInstance(new Object[]{});
// newInstanceメソッドを使ってインスタンス化する
// このnewInstanceメソッドの戻り方はconstructor変数の宣言時にしていした
// 総称型(ジェネリック)型になる

pConstractor.helloObject();
// インスタンス化できてメソッドにアクセスできている
}
}

そして呼び出した結果がこれ

え、やだっそんな無理やりインスタンス化なんて・・・らめぇええええ
ちょ、ちょっと!!なに勝手にインスタンス化してんのよ!!
・・・別にアンタのためにインスタンス化したわけじゃないんだからねっ!///


ね?簡単でしょ?
今回はprivateなコンストラクタにアクセスする方法を紹介したけどどうだったかな?
このコードでクラスとインスタンス、クラスに定義されたメンバーその辺りの関係が少しは見えてきたと思う。
一番のポイントはClassクラスの存在でクラス自体もJVM上にインスタンスとして存在していてそれに対して動的にアクセスしたりもできるって事。
Javaは静的型付けを強く意識しているのでこういうことはしないほうがいいんだろうけど、できなくもないんだよね。

0 件のコメント: