モジュール java.base
パッケージ java.lang.invoke

クラスMethodHandles.Lookup

java.lang.Object
java.lang.invoke.MethodHandles.Lookup
含まれているクラス:
MethodHandles

public static final class MethodHandles.Lookup extends Object
ルックアップ・オブジェクトは、メソッド・ハンドルの作成にアクセス・チェックが必要な場合のメソッド・ハンドル作成用ファクトリです。 メソッド・ハンドルでアクセス・チェックが実行されるのは、その呼出し時ではなく作成時です。 したがって、メソッド・ハンドルのアクセス制限は、メソッド・ハンドルの作成時に適用する必要があります。 それらの制限の適用先となる呼出し元クラスは、ルックアップ・クラスと呼ばれます。

メソッド・ハンドルを作成する必要のあるルックアップ・クラスは、MethodHandles.lookupを呼び出して自分用のファクトリを作成します。 Lookupファクトリ・オブジェクトの作成時には、ルックアップ・クラスのアイデンティティが決定され、その情報がLookupオブジェクト内にセキュアに格納されます。 その後、ルックアップ・クラス(またはその委譲先)は、Lookupオブジェクトのファクトリ・メソッドを使ってアクセス・チェックされたメンバーのメソッド・ハンドルを作成できます。 これには、ルックアップ・クラスに許可されるメソッド、コンストラクタ、およびフィールドがすべて含まれます(privateのものも含む)。

Lookupファクトリ・メソッド

Lookupオブジェクトのファクトリ・メソッドは、メソッド、コンストラクタ、およびフィールドのすべてのメジャーなユース・ケースに対応しています。 ファクトリ・メソッドによって作成される各メソッド・ハンドルは、特定のバイトコード動作と機能的に同等です。 (バイトコードの動作は、Java Virtual Machine仕様の5.4.3.5の項で説明されています。) これらのファクトリ・メソッドと、結果として得られるメソッド・ハンドルの動作との対応の概要を以下に示します:
ルックアップ・メソッドの動作
ルックアップ式 member バイトコード動作
lookup.findGetter(C.class,"f",FT.class) FT f;(T) this.f;
lookup.findStaticGetter(C.class,"f",FT.class) static
FT f;
(FT) C.f;
lookup.findSetter(C.class,"f",FT.class) FT f;this.f = x;
lookup.findStaticSetter(C.class,"f",FT.class) static
FT f;
C.f = arg;
lookup.findVirtual(C.class,"m",MT) T m(A*);(T) this.m(arg*);
lookup.findStatic(C.class,"m",MT) static
T m(A*);
(T) C.m(arg*);
lookup.findSpecial(C.class,"m",MT,this.class) T m(A*);(T) super.m(arg*);
lookup.findConstructor(C.class,MT) C(A*);new C(arg*);
lookup.unreflectGetter(aField) (static)?
FT f;
(FT) aField.get(thisOrNull);
lookup.unreflectSetter(aField) (static)?
FT f;
aField.set(thisOrNull, arg);
lookup.unreflect(aMethod) (static)?
T m(A*);
(T) aMethod.invoke(thisOrNull, arg*);
lookup.unreflectConstructor(aConstructor) C(A*);(C) aConstructor.newInstance(arg*);
lookup.unreflectSpecial(aMethod,this.class) T m(A*);(T) super.m(arg*);
lookup.findClass("C") class C { ... }C.class;
ここで、型Cはメンバーが検索されるクラスまたはインタフェースで、ルックアップ・メソッド内でrefcという名前のパラメータとして記述されています。 メソッド型MTは、戻り型Tと引数型シーケンスA*から構成されます。 コンストラクタも引数型シーケンスA*を持ち、型Cの新しく作成されたオブジェクトを返すと見なされます。 MTとフィールドの型FTはどちらも、typeという名前のパラメータとしてドキュメント化されています。 仮パラメータthisC型の自己参照を表しています。これは、存在する場合は常にメソッド・ハンドル呼出しの先頭の引数になります。 (一部のprotectedメンバーでは、thisの型がルックアップ・クラスに制限される場合があります。下記を参照してください。) 名前argは、メソッド・ハンドルのほかのすべての引数を表しています。 Core Reflection APIのコード例に含まれる名前thisOrNullは、アクセス対象のメソッドやフィールドがstaticの場合はnull参照を表し、それ以外の場合はthisを表します。 aMethodaFieldおよびaConstructorの名前は、Cに宣言された特定のメンバーに対応するリフレクト・オブジェクトを表します。

findClass操作のバイトコード動作は、ldc CONSTANT_Classのように、定数クラスのロードです。 動作は、メソッド・ハンドルとしてではなく、Class定数として直接表されます。

指定されたメンバーが可変引数(つまりメソッドまたはコンストラクタ)の場合、返されるメソッド・ハンドルも可変引数になります。 その他のすべての場合、返されるメソッド・ハンドルは固定引数になります。

ディスカッション: ルックアップされたメソッド・ハンドルと基礎となるクラス・メンバーとバイトコード動作との間の等価性が、いくつかの方法で壊れることがあります。

  • Cがルックアップ・クラスのローダーからシンボルでアクセスできない場合でも、ルックアップが成功することがあります(同等のJava式やバイト・コード定数が存在しない場合でも)。
  • 同様に、TまたはMTがルックアップ・クラスのローダーからシンボルでアクセスできない場合でも、ルックアップが成功することがあります。 たとえば、MethodHandle.invokeExactMethodHandle.invokeのルックアップは、要求された型とは無関係に常に成功します。
  • セキュリティ・マネージャがインストールされている場合、さまざまな理由でルックアップが禁止される可能性があります(以下を参照)。 一方、CONSTANT_MethodHandle定数でのldc命令はセキュリティ・マネージャ・チェックされません。
  • 参照されたメソッドに「非常に多い引数」がある場合、メソッド・ハンドルの作成はIllegalArgumentExceptionで失敗することがあります。これは、メソッド・ハンドルの型が「あまりにも多くのパラメータ」を持つためです。

アクセス・チェック

アクセス・チェックは、メソッド・ハンドルの作成時にLookupのファクトリ・メソッド内で適用されます。 これが、Core Reflection APIとの重要な違いです(java.lang.reflect.Method.invokeではすべての呼出しで、すべての呼出し元に対してアクセス・チェックが実行される)。

すべてのアクセス・チェックはLookupオブジェクトから始まりますが、このオブジェクトでは、記録されているルックアップ・クラスとすべてのメソッド・ハンドル作成要求とが照合されます。 単一のLookupオブジェクトを使って任意の数のアクセス・チェック済みメソッド・ハンドルを作成できます(すべては単一のルックアップ・クラスに基づいてチェックされる)。

Lookupオブジェクトは、ほかの信頼できるコード(メタオブジェクト・プロトコルなど)と共有できます。 共有Lookupオブジェクトは、メソッド・ハンドルを作成する機能を、ルックアップ・クラスのprivateメンバーに委譲します。 特権付きコードがLookupオブジェクトを使用する場合でも、アクセス・チェックは元のルックアップ・クラスの特権に限定されます。

ルックアップが失敗することがあります。包含するクラスにルックアップ・クラスからアクセスできない、目的のクラス・メンバーが見つからない、目的のクラス・メンバーにルックアップ・クラスからアクセスできない、またはルックアップ・オブジェクトが信頼されていないためメンバーにアクセスためです。 finalフィールドに対するフィールドのsetter関数の場合、finalityは、アクセス制御の一種として扱われ、Lookup.unreflectSetterの特別なケースを除いてルックアップは失敗します。 いずれの場合も、試みられたルックアップからReflectiveOperationExceptionがスローされます。 具体的なクラスは次のいずれかになります。

  • NoSuchMethodException-要求されたメソッドが存在しない場合
  • NoSuchFieldException-要求されたフィールドが存在しない場合
  • IllegalAccessException-メンバーは存在しているが、アクセス・チェックが失敗した場合

一般に、メソッドMがメソッド・ハンドルをルックアップできる条件は、ルックアップ・クラスがMへの呼出しをコンパイル、検証、解決できる条件ほど制限的ではありません。 JVMがNoSuchMethodErrorなどの例外をスローするとき、メソッド・ハンドル・ルックアップは一般にNoSuchMethodExceptionなどの対応するチェック例外をスローします。 また、ルックアップの結果実行されるメソッド・ハンドル呼出しの効果は、コンパイル、検証および解決されたM呼出しを実行するのとまったく同じです。 フィールドとコンストラクタについても同じことが言えます。

ディスカッション: アクセス・チェックは、名前付きのリフレクトされたメソッド、コンストラクタ、およびフィールドにのみ適用されます。 MethodHandle.asTypeなど、他のメソッド・ハンドル作成メソッドは、アクセス・チェックを必要とせず、Lookupオブジェクトから独立して使用されます。

目的のメンバーがprotectedの場合は、通常のJVMルールが適用されます。たとえば、ルックアップ・クラスが目的のメンバーと同じパッケージ内に存在する必要があるという要件や、そのメンバーを継承する必要があります。 (Java Virtual Machine仕様の4.9.25.4.3.5および6.4の項を参照してください。) また、目的のメンバーが別のパッケージ内にある非staticフィールドまたはメソッドである場合、結果となるメソッド・ハンドルはルックアップ・クラスまたはそのいずれかのサブクラスのオブジェクトにしか適用されない可能性があります。 この要件は、先頭のthisパラメータの型をC (必ずルックアップ・クラスのスーパー・クラスになる)からルックアップ・クラス自体にナロー変換することで強制されます。

JVMは同様の要件をinvokespecial命令に適用します(レシーバ引数が解決済メソッドおよび現在のクラスに一致する必要がある)。 さらにこの要件は、先頭パラメータの型を結果のメソッド・ハンドルにナロー変換することで適用されます。 (Java Virtual Machine仕様の4.10.1.9の項を参照してください。)

JVMは、特殊な名前("<init>""<clinit>")を持つ内部メソッドとして、コンストラクタおよび静的イニシャライザ・ブロックを表します。 呼出し命令の内部構文は、そのような内部メソッドを通常のメソッドのように参照することを許可しますが、JVMバイトコード・ベリファイアはそれらを拒否します。 そのような内部メソッドをルックアップはNoSuchMethodExceptionを生成します。

ネストされた型間の関係がNestHost属性とNestMembers属性(Java Virtual Machine仕様の4.7.28および4.7.29の項を参照してください。)を介して直接表現される場合、関連付けられたLookupオブジェクトは、ルックアップ・クラスとそのすべてのネストされた(Class.getNestHostを参照してください)への直接アクセスを提供します。 それ以外の場合、ネストされたクラス間のアクセスは、同じネスト内の別のクラスのprivateメソッドにアクセスするラッパー・メソッドを作成するJavaコンパイラによって取得されます。 たとえば、ネストされたクラスC.Dはほかの関連クラス(CC.D.EC.Bなど)の内部のprivateメンバーにアクセスできますが、Javaコンパイラがそれらの関連クラス内でラッパー・メソッドを生成しなければいけない可能性があります。 このような場合、C.E上のLookupオブジェクトはそのプライベート・メンバーにアクセスできません。 この制限の回避方法の1つがLookup.inメソッドであり、これを使うことで、特権レベルを特別に上げることなくC.Eのルックアップをそうしたほかのクラスのいずれかのルックアップに変換できます。

指定されたルックアップ・オフセットに許可されるアクセスは、そのlookupModesセットに基づいて、ルックアップ・クラスから通常アクセスできるメンバー・サブセットに制限される場合があります。 たとえば、publicLookupメソッドは、エクスポートされたパッケージのパブリック・クラスのパブリック・メンバーへのアクセスのみを許可されるルックアップ・オブジェクトを生成します。 呼出し元依存メソッドlookupは、サポートされているすべてのバイトコード動作をエミュレートする、呼出し元クラスに関連するあらゆる機能を持つルックアップ・オブジェクトを生成します。 また、Lookup.inメソッドは元のルックアップ・オブジェクトよりアクセス・モードが少ないルックアップ・オブジェクトを生成できます。

プライベート・アクセスとモジュール・アクセスについての説明:そのルックアップ・モードprivateメンバー(ネストのプライベート・メンバーを含む)へのアクセスの可能性が含まれていると、参照にはプライベート・アクセスがあると呼びます。 ドキュメント内の関連メソッドに記述されているように、privateアクセスを持つルックアップのみが次の機能を持ちます。

同様に、モジュール・アクセスを持つ参照では、元の参照作成者が参照クラスと同じモジュール内のメンバーであることが保証されます。

プライベートおよびモジュールへのアクセスは、独立して決定されるモードであり、参照には両方、またはどちらもない可能性があります。 両方のアクセス・モードを持つ参照は、「完全な権限アクセス」と言われます。

「元のアクセス」を使用したルックアップでは、このルックアップが元のルックアップ・クラスによって作成され、ブートストラップ・メソッドがVMによって呼び出されることが保証されます。 元のアクセス権を持つこのようなルックアップには、次の追加機能を持つプライベートおよびモジュール・アクセス権もあります:

これらのことが許可されるのは、privateアクセスを持つルックアップ・オブジェクトから元のクラスまで安全にたどることができ、そのバイトコード動作およびJava言語アクセス権をメソッド・ハンドルが確実に判断してエミュレートできるという事実の結果です。

モジュール間参照

あるモジュールM1の参照クラスが別のモジュールM2にあるクラスにアクセスする場合、アクセス・モード・ビットの範囲を超えて余分なアクセス・チェックが実行されます。 PUBLICモードのLookupおよびM1の参照クラスでは、M2M1に対して読取り可能である場合、およびタイプがM1以上にエクスポートされるM2のパッケージ内にある場合、M2のパブリック・タイプにアクセスできます。

C上のLookupでは、Lookup.inおよびMethodHandles.privateLookupInメソッドを介してターゲット・クラスに「テレ・ポート」を実行することもできます。 モジュール間でのポートレット化では、前のルックアップ・クラスとして元の参照クラスが常に記録され、MODULEアクセス権が削除されます。 ターゲット・クラスがルックアップ・クラスCと同じモジュール内にある場合、そのターゲット・クラスは新しいルックアップ・クラスになり、以前のルックアップ・クラスに変更はありません。 ターゲット・クラスがM1 (Cのモジュール)とは異なるモジュールにある場合は、Cが新しい以前の参照クラスになり、ターゲット・クラスが新しい参照クラスになります。 その場合、M0に以前のルックアップ・クラスがすでに存在し、M1M2と異なる場合は、結果のルックアップがすべての権限をドロップします。 たとえば、

Lookup lookup = MethodHandles.lookup();   // in class C
Lookup lookup2 = lookup.in(D.class);
MethodHandle mh = lookup2.findStatic(E.class, "m", MT);

MethodHandles.lookup()ファクトリ・メソッドにより、nullの前のルックアップ・クラスを持つLookupオブジェクトが生成されます。lookup.in(D.class)は、クラスClookupを、権限を昇格させずにDクラスに変換します。 CDが同じモジュールの場合、lookup2Dを新しい参照クラスとして記録し、元のlookupと同じ前の参照クラスを維持します。存在しない場合は、nullになります。

あるネストされたクラスから別のネストされたクラスへのLookupテレ・ポートを実行すると、PRIVATEアクセス権が削除されます。 あるパッケージのクラスから別のパッケージへのLookupテレ・ポートを実行すると、PACKAGEにアクセスできなくなります。 あるモジュールのクラスから別のモジュールへのLookupポートが提供されると、MODULEアクセス権が切断されます。 モジュール間のテレティングでは、新しいルックアップ・クラスのモジュールと古いルックアップ・クラスのモジュールの両方から、エクスポートされていないクラスにアクセスする機能をドロップします。そうすると、LookupではPUBLICアクセスのみが可能になります。 Lookupは、参照クラスのモジュール内のクラスおよび前のクラス参照のモジュールに電話をかけて移動できます。 モジュール間の通信は、アクセス権を減らすことのみができますが、増やすことはできません。 3つ目のモジュールに通信している場合は、すべてのアクセスを削除します。

前述の例では、CDが異なるモジュールにある場合、lookup2Dをその参照クラスとして記録し、lookup2にはPUBLICアクセスのみがあります。lookup2は、C 'モジュールおよびD'モジュールの他のクラスへの通信を行えます。 Eクラスが3番目のモジュールにある場合、lookup2.in(E.class)はアクセスなしでELookupを作成し、lookup2の参照クラスDは以前の参照クラスとして記録されます。

モジュール間の通信によって、参照クラスと前の参照クラスの両方が(下記参照)に同等にアクセスできるパブリック・タイプにアクセスが制限されます。

MethodHandles.privateLookupIn(T.class, lookup)を使用すると、クラスCからクラスTlookupをテレポートし、ルックアップ・クラスでTに対して「深い反射」を実行できる場合は、「プライベート・アクセス」を使用して新しいLookupを生成できます。 lookupprivateLookupInをコールするには、MODULEおよびPRIVATEアクセス権が必要です。 モジュールM1C上でのlookupは、M1内のすべてのクラスに対してディープ・リフレクションを実行できます。 TM1に含まれる場合、privateLookupInでは、完全な機能を備えたT上に新しいLookupが生成されます。 Cでのlookupは、M1M2M2 opensM1を含むパッケージを読み取る場合に、別のモジュールM2Tをさらに深くリフレクションすることもできます。 T が新しい参照クラスになり、Cが新しい以前の参照クラスになり、MODULEのアクセス権が結果のLookupから削除されます。 結果のLookupを使用すると、Lookup::inをコールして、別の参照クラスにメンバー参照またはテレ・ポートを実行できます。 ただし、MODULEアクセス権がないため、privateLookupInをコールして別のプライベートLookupを取得するためには使用できません。

privateLookupInによって返されるLookupオブジェクトは、Tの実行時パッケージで「クラスの定義」に許可されます。 パッケージを別のモジュールに開く場合は、M2内のほかのメンバーと同じ完全なアクセス権を持つように、特に注意してください。

モジュール間アクセス・チェック

PUBLICを含むLookupまたはUNCONDITIONALモードを使用すると、モジュール間アクセスが可能になります。 アクセス・チェックは、参照クラスと前の参照クラス(存在する場合)の両方に対して実行されます。

UNCONDITIONALモードのLookupでは、すべてのモジュールのパブリック・タイプは「無条件でエクスポート」のパッケージの中にアクセスできます。

M1LCLookupに以前の参照クラスがない場合、PUBLICモードで参照すると、M1で読取り可能なモジュール内のすべてのパブリック・タイプにアクセスでき、タイプは少なくともM1にエクスポートされるパッケージ内にあります。

M1LCLCに以前の参照クラスM0の前の参照クラスPLCがある場合、PUBLICモードでの参照は、PUBLICモードで参照すると、M0からアクセス可能なすべてのパブリック・タイプを使用して、M1でアクセスできるすべてのパブリック・タイプの共通部分にアクセスできます。 M0M1を読み取るため、アクセス可能なタイプのセットには次のものがあります:

  • M1からの無条件エクスポートのパッケージ
  • M1M0を読み込む場合、M0から無条件エクスポート・パッケージをエクスポート
  • M0M1の両方がM2を読み上げた場合、第3のモジュールM2から無条件エクスポートのパッケージをエクスポート
  • レイヤー化されたエクスポート済パッケージ: M1からM0
  • M1M0を読み取った場合に、M0からM1に並列エクスポートされたパッケージ
  • M0M1の両方がM2を読む場合、3番目のモジュールM2からM0M1の両方にレイヤー化されたエクスポートされたパッケージ

アクセス・モード

次の表に、ファクトリまたは変換のいずれかのメソッドによって生成されるLookupのアクセス・モードを示します:
アクセス・モード・サマリー
オブジェクトの参照 original protected private package module public
CCL = MethodHandles.lookup() ORI PRO PRI PAC MOD 1R
CL.in(C1)同じパッケージ PAC MOD 1R
CL.in(C1)同じモジュール MOD 1R
CL.in(D)異なるモジュール 2R
モジュールにCL.in(D).in(C)ホップ・バック 2R
PRI1 = privateLookupIn(C1,CL) PRO PRI PAC MOD 1R
PRI1a = privateLookupIn(C,PRI1) PRO PRI PAC MOD 1R
PRI1.in(C1)同じパッケージ PAC MOD 1R
PRI1.in(C1)異なるパッケージ MOD 1R
PRI1.in(D)異なるモジュール 2R
PRI1.dropLookupMode(PROTECTED) PRI PAC MOD 1R
PRI1.dropLookupMode(PRIVATE) PAC MOD 1R
PRI1.dropLookupMode(PACKAGE) MOD 1R
PRI1.dropLookupMode(MODULE) 1R
PRI1.dropLookupMode(PUBLIC) none
PRI2 = privateLookupIn(D,CL) PRO PRI PAC 2R
privateLookupIn(D,PRI1) PRO PRI PAC 2R
privateLookupIn(C,PRI2)は失敗 IAE
PRI2.in(D2)同じパッケージ PAC 2R
PRI2.in(D2)異なるパッケージ 2R
モジュールにPRI2.in(C1)ホップ・バック 2R
PRI2.in(E)ホップから3番目のモジュール none
PRI2.dropLookupMode(PROTECTED) PRI PAC 2R
PRI2.dropLookupMode(PRIVATE) PAC 2R
PRI2.dropLookupMode(PACKAGE) 2R
PRI2.dropLookupMode(MODULE) 2R
PRI2.dropLookupMode(PUBLIC) none
CL.dropLookupMode(PROTECTED) PRI PAC MOD 1R
CL.dropLookupMode(PRIVATE) PAC MOD 1R
CL.dropLookupMode(PACKAGE) MOD 1R
CL.dropLookupMode(MODULE) 1R
CL.dropLookupMode(PUBLIC) none
PUB = publicLookup() U
PUB.in(D)異なるモジュール U
PUB.in(D).in(E) 3番目のモジュール U
PUB.dropLookupMode(UNCONDITIONAL) none
privateLookupIn(C1,PUB)は失敗 IAE
ANY.in(X)(アクセスできないXの場合) none

ノート:

  • CクラスとC1クラスは、モジュールM1内にありますが、DD2はモジュールM2で、EはモジュールM3にあります。 X は、参照がアクセス不可能なクラスを探します。 ANY は、サンプル・ルックアップの表します。
  • ORIORIGINALビット・セットを示し、PROPROTECTEDビット・セットを示し、PRIPRIVATEビット・セットを示し、PACPACKAGEビット・セットを示し、MODMODULEビット・セットを示し、1R2RPUBLICコード・セットを示し、UUNCONDITIONALビット・セットを示し、IAEはスローされたIllegalAccessExceptionを示します。
  • パブリック・アクセスには、次の3種類があります:
    • 無条件(U): ルックアップは読みやすくなっています。 ルックアップにnull前のルックアップ・クラスがある場合。
    • one-module-reads (1R): モジュール・アクセス・チェックは、参照クラスに対して実行します。 ルックアップにnull前のルックアップ・クラスがある場合。
    • two-module-reads (2R): モジュール・アクセス・チェックは、参照クラスおよび前の参照クラスについて実行されます。 ルックアップに、現在のルックアップ・クラスとは異なるモジュール内にあるnull以外の前のルックアップ・クラスが含まれています。
  • 3つ目のモジュールに到達しようとすると、すべてのアクセスが失われます。
  • Lookup::inでは、ターゲット・クラスXにアクセスできない場合、すべてのアクセス・モードが削除されます。

セキュリティ・マネージャとの対話

バイト・コード命令は関連クラス・ローダー内のクラスしか参照できませんが、このAPIでは、Classオブジェクトへの参照が使用可能であるかぎり、任意のクラス内のメソッドを検索できます。 そのようなクロスローダー参照は、Core Reflection APIでも可能ですが、invokestaticgetfieldなどのバイト・コード命令では不可能です。 アプリケーション内でそのようなクロスローダー参照をチェックできるようにするためのセキュリティ・マネージャAPIが存在します。 それらのチェックは、MethodHandles.Lookup APIと(Class上に存在する) Core Reflection APIの両方に適用されます。

セキュリティ・マネージャが存在する場合、メンバーおよびクラスのルックアップは追加チェックの対象となります。 セキュリティ・マネージャに対して1回から3回の呼出しが行われます。 これらの呼出しのいずれも、SecurityExceptionをスローすることでアクセスを拒否できます。 smgrをセキュリティ・マネージャとして、lookcを現在のルックアップ・オブジェクトのルックアップ・クラスとして、refcをメンバーを検索する包含クラスとして、defcをメンバーが実際に定義されるクラスとして定義してください。 (クラスまたはその他の型にアクセスする場合、refcおよびdefc値はクラス自体です。) 現在の参照オブジェクトに「完全な権限アクセス」が含まれない場合、lookc値は「存在しない」と定義されます。 次のルールに従って呼出しが行われます。

  • ステップ1: lookcが存在しない場合、またはそのクラス・ローダーがrefcのクラス・ローダーと同じでもその祖先でもない場合は、smgr.checkPackageAccess(refcPkg)が呼び出されます(refcPkgrefcのパッケージ)。
  • ステップ2a: 取得されたメンバーがパブリックでなく、lookcが存在しない場合は、RuntimePermission("accessDeclaredMembers")を指定したsmgr.checkPermissionがコールされます。
  • ステップ2b: 取得されたクラスにnullクラス・ローダーがあり、lookcが存在しない場合は、RuntimePermission("getClassLoader")を指定したsmgr.checkPermissionがコールされます。
  • ステップ3: 取得されるメンバーがpublicでなく、lookcが存在せず、defcrefcが異なる場合は、smgr.checkPackageAccess(defcPkg)が呼び出されます(defcPkgdefcのパッケージ)。
セキュリティ・チェックは他のアクセス・チェックを合格した後に実行されます。 したがって、前述のルールは、publicであるメンバーまたはクラス、あるいはメンバーまたはクラスにアクセスする権限を持つルックアップ・クラスからアクセスされているメンバーまたはクラスを前提としています。

セキュリティ・マネージャが存在し、現在のルックアップ・オブジェクトに「完全な権限アクセス」がない場合、defineClassdefineHiddenClassdefineHiddenClassWithClassDatasmgr.checkPermissionRuntimePermission("defineClass")とともにコールします。

呼出し元依存メソッド

少数のJavaメソッドに、呼出し元依存性という特殊なプロパティがあります。 呼出し元依存メソッドは、その直接の呼出し元の識別情報によって異なる動作をする可能性があります。

呼出し元依存メソッドのメソッド・ハンドルが要求された場合は、バイトコード動作の一般ルールが適用されますが、ルックアップ・クラスは特別な方法で考慮されます。 結果のメソッド・ハンドルはルックアップ・クラスに含まれている命令から呼び出されたように動作し、呼出し元依存メソッドはルックアップ・クラスを検出します。 (一方、メソッド・ハンドルのインボーカは無視されます。) したがって、呼出し元依存メソッドの場合は、ルックアップ・クラスごとにメソッド・ハンドルの動作が異なる可能性があります。

ルックアップ・オブジェクトがpublicLookup()の場合や、「元のアクセス」を使用しないその他のルックアップ・オブジェクトの場合は、ルックアップ・クラスが無視されます。 そのような場合、呼出し元依存メソッド・ハンドルを作成できず、アクセスは禁止され、ルックアップはIllegalAccessExceptionで失敗します。

ディスカッション: たとえば、呼出し元依存メソッドClass.forName(x)は、それを呼び出すクラスのクラス・ローダーに応じて、返されるクラスを異なったり、スローされる例外が異なったりする可能性があります。 Class.forNameのpublicルックアップは失敗します。そのバイトコード動作を判別するための妥当な方法がないためです。

広範囲な共有のためにメソッド・ハンドルをキャッシュするアプリケーションの場合は、publicLookup()を使用してメソッド・ハンドルを作成することをお薦めします。 Class.forNameのルックアップがある場合、それは失敗し、アプリケーションはその場合に適切なアクションを取る必要があります。 たとえば、後のルックアップ(おそらくブートストラップ・メソッドの呼び出し中)が呼出し元固有の識別情報を組み込むことで、メソッドをアクセス可能にするアクションです。

関数MethodHandles.lookupは呼出し元依存なので、ルックアップ用の安全な基盤が存在できます。 JSR 292 API内の他のほとんどすべてのメソッドはルックアップ・オブジェクトに依存してアクセス要求をチェックします。