public static final class MethodHandles.Lookup extends Object
メソッド・ハンドルを作成する必要のあるルックアップ・クラスは、MethodHandles.lookupを呼び出して自分用のファクトリを作成します。Lookupファクトリ・オブジェクトの作成時には、ルックアップ・クラスのアイデンティティが決定され、その情報がLookupオブジェクト内にセキュアに格納されます。その後、ルックアップ・クラス(またはその委譲先)は、Lookupオブジェクトのファクトリ・メソッドを使ってアクセス・チェックされたメンバーのメソッド・ハンドルを作成できます。これには、ルックアップ・クラスに許可されるメソッド、コンストラクタ、およびフィールドがすべて含まれます(privateのものも含む)。
Lookupオブジェクトのファクトリ・メソッドは、メソッド、コンストラクタ、およびフィールドのすべてのメジャーなユース・ケースに対応しています。ファクトリ・メソッドによって作成される各メソッド・ハンドルは、特定のバイトコード動作と機能的に同等です。(バイトコード動作は『Java Virtual Machine Specification』のセクション5.4.3.5に記述されています。)これらのファクトリ・メソッドと、結果となるメソッド・ハンドルの動作との対応関係のサマリーを、次に示します。
| ルックアップ式 | メンバー | バイトコード動作 |
|---|---|---|
lookup.findGetter(C.class,"f",FT.class) |
FT f; | (T) this.f; |
lookup.findStaticGetter(C.class,"f",FT.class) |
staticFT f; | (T) C.f; |
lookup.findSetter(C.class,"f",FT.class) |
FT f; | this.f = x; |
lookup.findStaticSetter(C.class,"f",FT.class) |
staticFT f; | C.f = arg; |
lookup.findVirtual(C.class,"m",MT) |
T m(A*); | (T) this.m(arg*); |
lookup.findStatic(C.class,"m",MT) |
staticT 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.unreflect(aMethod) |
(static)?T m(A*); | (T) aMethod.invoke(thisOrNull, arg*); |
Cはメンバーが検索されるクラスまたはインタフェースで、ルックアップ・メソッド内でrefcという名前のパラメータとして記述されています。メソッド型MTは、戻り型Tと引数型シーケンスA*から構成されます。コンストラクタも引数型シーケンスA*を持ち、型Cの新しく作成されたオブジェクトを返すと見なされます。MTとフィールドの型FTはどちらも、typeという名前のパラメータとしてドキュメント化されています。仮パラメータthisはC型の自己参照を表しています。これは、存在する場合は常にメソッド・ハンドル呼出しの先頭の引数になります。(一部のprotectedメンバーでは、thisの型がルックアップ・クラスに制限される場合があります。下記を参照してください。)名前argは、メソッド・ハンドルのほかのすべての引数を表しています。Core Reflection APIのコード例に含まれる名前thisOrNullは、アクセス対象のメソッドやフィールドがstaticの場合はnull参照を表し、それ以外の場合はthisを表します。名前aMethod、aField、およびaConstructorは、指定されたメンバーに対応するリフレクション・オブジェクトを表しています。
指定されたメンバーが可変引数(つまりメソッドまたはコンストラクタ)の場合、返されるメソッド・ハンドルも可変引数になります。その他のすべての場合、返されるメソッド・ハンドルは固定引数になります。
ディスカッション: ルックアップされたメソッド・ハンドルと基礎となるクラス・メンバーとバイトコード動作との間の等価性が、いくつかの方法で壊れることがあります。
Cがルックアップ・クラスのローダーからシンボルでアクセスできない場合でも、ルックアップが成功することがあります(同等のJava式やバイト・コード定数が存在しない場合でも)。
TまたはMTがルックアップ・クラスのローダーからシンボルでアクセスできない場合でも、ルックアップが成功することがあります。たとえば、MethodHandle.invokeExactやMethodHandle.invokeのルックアップは、要求された型とは無関係に常に成功します。
CONSTANT_MethodHandle定数でのldc命令はセキュリティ・マネージャ・チェックされません。
Lookupのファクトリ・メソッド内で適用されます。これが、Core Reflection APIとの重要な違いです(java.lang.reflect.Method.invokeではすべての呼出しで、すべての呼出し元に対してアクセス・チェックが実行される)。
すべてのアクセス・チェックはLookupオブジェクトから始まりますが、このオブジェクトでは、記録されているルックアップ・クラスとすべてのメソッド・ハンドル作成要求とが照合されます。単一のLookupオブジェクトを使って任意の数のアクセス・チェック済みメソッド・ハンドルを作成できます(すべては単一のルックアップ・クラスに基づいてチェックされる)。
Lookupオブジェクトは、ほかの信頼できるコード(メタオブジェクト・プロトコルなど)と共有できます。共有Lookupオブジェクトは、メソッド・ハンドルを作成する機能を、ルックアップ・クラスのprivateメンバーに委譲します。特権付きコードがLookupオブジェクトを使用する場合でも、アクセス・チェックは元のルックアップ・クラスの特権に限定されます。
ルックアップが失敗することがあります。包含するクラスにルックアップ・クラスからアクセスできない、目的のクラス・メンバーが見つからない、目的のクラス・メンバーにルックアップ・クラスからアクセスできない、またはルックアップ・オブジェクトが信頼されていないためメンバーにアクセスためです。いずれの場合も、試みられたルックアップからReflectiveOperationExceptionがスローされます。具体的なクラスは次のいずれかになります。
一般に、メソッドMがメソッド・ハンドルをルックアップできる条件は、ルックアップ・クラスがMへの呼出しをコンパイル、検証、解決できる条件ほど制限的ではありません。JVMがNoSuchMethodErrorなどの例外をスローするとき、メソッド・ハンドル・ルックアップは一般にNoSuchMethodExceptionなどの対応するチェック例外をスローします。また、ルックアップの結果実行されるメソッド・ハンドル呼出しの効果は、コンパイル、検証および解決されたM呼出しを実行するのとまったく同じです。フィールドとコンストラクタについても同じことが言えます。
ディスカッション: アクセス・チェックは、名前付きのリフレクトされたメソッド、コンストラクタ、およびフィールドにのみ適用されます。MethodHandle.asTypeなど、他のメソッド・ハンドル作成メソッドは、アクセス・チェックを必要とせず、Lookupオブジェクトから独立して使用されます。
目的のメンバーがprotectedである場合、ルックアップ・クラスが目的のメンバーと同じパッケージに含まれているか、そのメンバーを継承している必要があるという要件など、通常のJVMルールが適用されます。(『Java Virtual Machine Specification』のセクション4.9.2、5.4.3.5、および6.4を参照してください。)また、目的のメンバーが別のパッケージ内にある非staticフィールドまたはメソッドである場合、結果となるメソッド・ハンドルはルックアップ・クラスまたはそのいずれかのサブクラスのオブジェクトにしか適用されない可能性があります。この要件は、先頭のthisパラメータの型をC (必ずルックアップ・クラスのスーパー・クラスになる)からルックアップ・クラス自体にナロー変換することで強制されます。
JVMは同様の要件をinvokespecial命令に適用します(レシーバ引数が解決済メソッドおよび現在のクラスに一致する必要がある)。さらにこの要件は、先頭パラメータの型を結果のメソッド・ハンドルにナロー変換することで適用されます。(『Java Virtual Machine Specification』のセクション4.10.1.9を参照してください。)
JVMは、コンストラクタおよびstaticイニシャライザ・ブロックを、特殊な名前("<init>"と"<clinit>")を持つ内部メソッドとして表します。呼出し命令の内部構文は、そのような内部メソッドを通常のメソッドのように参照することを許可しますが、JVMバイトコード・ベリファイアはそれらを拒否します。そのような内部メソッドをルックアップはNoSuchMethodExceptionを生成します。
場合によっては、ネストされたクラス間のアクセスは、Javaコンパイラが、別のクラスのprivateメソッドにアクセスするためのラッパー・メソッドを同じトップ・レベルの宣言内で作成することによって得られます。たとえば、ネストされたクラスC.Dはほかの関連クラス(C、C.D.E、C.Bなど)の内部のprivateメンバーにアクセスできますが、Javaコンパイラがそれらの関連クラス内でラッパー・メソッドを生成しなければいけない可能性があります。そのような場合、C.EのLookupオブジェクトはそれらのprivateメンバーにアクセスできません。この制限の回避方法の1つがLookup.inメソッドであり、これを使うことで、特権レベルを特別に上げることなくC.Eのルックアップをそうしたほかのクラスのいずれかのルックアップに変換できます。
指定されたルックアップ・オフセットに許可されるアクセスは、そのlookupModesセットに基づいて、ルックアップ・クラスから通常アクセスできるメンバー・サブセットに制限される場合があります。たとえば、publicLookupメソッドが生成するルックアップ・オブジェクトは、publicクラスのpublicメンバーへのアクセスのみが許可されます。呼出し元依存メソッドlookupは、サポートされているすべてのバイトコード動作をエミュレートする、呼出し元クラスに関連するあらゆる機能を持つルックアップ・オブジェクトを生成します。また、Lookup.inメソッドは元のルックアップ・オブジェクトよりアクセス・モードが少ないルックアップ・オブジェクトを生成できます。
privateアクセスのディスカッション: ルックアップ・モードにprivateメンバーにアクセスする可能性が含まれる場合、そのルックアップはprivateアクセスを持つと言います。ドキュメント内の関連メソッドに記述されているように、privateアクセスを持つルックアップのみが次の機能を持ちます。
Class.forNameなど)を呼び出すメソッド・ハンドルを作成する
invokespecial命令をエミュレートするメソッド・ハンドルを作成する
委譲されたルックアップ・オブジェクトを作成する
これらのことが許可されるのは、privateアクセスを持つルックアップ・オブジェクトから元のクラスまで安全にたどることができ、そのバイトコード動作およびJava言語アクセス権をメソッド・ハンドルが確実に判断してエミュレートできるという事実の結果です。
Classオブジェクトへの参照が使用可能であるかぎり、任意のクラス内のメソッドを検索できます。そのようなクロスローダー参照は、Core Reflection APIでも可能ですが、invokestaticやgetfieldなどのバイト・コード命令では不可能です。アプリケーション内でそのようなクロスローダー参照をチェックできるようにするためのセキュリティ・マネージャAPIが存在します。それらのチェックは、MethodHandles.Lookup APIと(Class上に存在する) Core Reflection APIの両方に適用されます。
セキュリティ・マネージャが存在する場合、メンバー・ルックアップに対して追加のチェックが行われます。セキュリティ・マネージャに対して1回から3回の呼出しが行われます。これらの呼出しのいずれも、SecurityExceptionをスローすることでアクセスを拒否できます。smgrをセキュリティ・マネージャとして、lookcを現在のルックアップ・オブジェクトのルックアップ・クラスとして、refcをメンバーを検索する包含クラスとして、defcをメンバーが実際に定義されるクラスとして定義してください。値lookcは、現在のルックアップ・オブジェクトがprivateアクセスを持たない場合は存在しないとして定義されます。次のルールに従って呼出しが行われます。
lookcが存在しない場合、またはそのクラス・ローダーがrefcのクラス・ローダーと同じでもその祖先でもない場合は、smgr.checkPackageAccess(refcPkg)が呼び出されます(refcPkgはrefcのパッケージ)。
lookcが存在しない場合は、RuntimePermission("accessDeclaredMembers")でsmgr.checkPermissionが呼び出されます。
lookcが存在せず、defcとrefcが異なる場合は、smgr.checkPackageAccess(defcPkg)が呼び出されます(defcPkgはdefcのパッケージ)。
呼出し元依存メソッドのメソッド・ハンドルが要求された場合は、バイトコード動作の一般ルールが適用されますが、ルックアップ・クラスは特別な方法で考慮されます。結果のメソッド・ハンドルはルックアップ・クラスに含まれている命令から呼び出されたように動作し、呼出し元依存メソッドはルックアップ・クラスを検出します。(一方、メソッド・ハンドルのインボーカは無視されます。)したがって、呼出し元依存メソッドの場合は、ルックアップ・クラスごとにメソッド・ハンドルの動作が異なる可能性があります。
ルックアップ・オブジェクトがpublicLookup()の場合、またはprivateアクセスなしのいくつかの他のルックアップ・オブジェクトの場合、そのルックアップ・クラスは無視されます。そのような場合、呼出し元依存メソッド・ハンドルを作成できず、アクセスは禁止され、ルックアップはIllegalAccessExceptionで失敗します。
ディスカッション: たとえば、呼出し元依存メソッドClass.forName(x)は、それを呼び出すクラスのクラス・ローダーに応じて、返されるクラスを異なったり、スローされる例外が異なったりする可能性があります。Class.forNameのpublicルックアップは失敗します。そのバイトコード動作を判別するための妥当な方法がないためです。
広範囲な共有のためにメソッド・ハンドルをキャッシュするアプリケーションの場合は、publicLookup()を使用してメソッド・ハンドルを作成することをお薦めします。Class.forNameのルックアップがある場合、それは失敗し、アプリケーションはその場合に適切なアクションを取る必要があります。たとえば、後のルックアップ(おそらくブートストラップ・メソッドの呼び出し中)が呼出し元固有の識別情報を組み込むことで、メソッドをアクセス可能にするアクションです。
関数MethodHandles.lookupは呼出し元依存なので、ルックアップ用の安全な基盤が存在できます。JSR 292 API内の他のほとんどすべてのメソッドはルックアップ・オブジェクトに依存してアクセス要求をチェックします。
| 修飾子と型 | フィールドと説明 |
|---|---|
static int |
PACKAGE
packageアクセス(デフォルト・アクセス)を表す単一ビット・マスク(lookupModesの結果に寄与する可能性がある)。 |
static int |
PRIVATE
privateアクセスを表す単一ビット・マスク(lookupModesの結果に寄与する可能性がある)。 |
static int |
PROTECTED
protectedアクセスを表す単一ビット・マスク(lookupModesの結果に寄与する可能性がある)。 |
static int |
PUBLIC
publicアクセスを表す単一ビット・マスク(lookupModesの結果に寄与する可能性がある)。 |
| 修飾子と型 | メソッドと説明 |
|---|---|
MethodHandle |
bind(Object receiver, String name, MethodType type)
非staticメソッドの早期バインド・メソッド・ハンドルを生成します。
|
MethodHandle |
findConstructor(Class<?> refc, MethodType type)
指定された型のコンストラクタを使ってオブジェクトの作成と初期化を行うメソッド・ハンドルを生成します。
|
MethodHandle |
findGetter(Class<?> refc, String name, Class<?> type)
非staticフィールドに対する読取りアクセスを提供するメソッド・ハンドルを生成します。
|
MethodHandle |
findSetter(Class<?> refc, String name, Class<?> type)
非staticフィールドに対する書込みアクセスを提供するメソッド・ハンドルを生成します。
|
MethodHandle |
findSpecial(Class<?> refc, String name, MethodType type, Class<?> specialCaller)
仮想メソッドの早期にバインドされるメソッド・ハンドルを生成します。
|
MethodHandle |
findStatic(Class<?> refc, String name, MethodType type)
staticメソッドのメソッド・ハンドルを生成します。
|
MethodHandle |
findStaticGetter(Class<?> refc, String name, Class<?> type)
staticフィールドに対する読取りアクセスを提供するメソッド・ハンドルを生成します。
|
MethodHandle |
findStaticSetter(Class<?> refc, String name, Class<?> type)
staticフィールドに対する書込みアクセスを提供するメソッド・ハンドルを生成します。
|
MethodHandle |
findVirtual(Class<?> refc, String name, MethodType type)
仮想メソッドのメソッド・ハンドルを生成します。
|
MethodHandles.Lookup |
in(Class<?> requestedLookupClass)
指定された新しいルックアップ・クラスでルックアップを作成します。
|
Class<?> |
lookupClass()
ルックアップを実行しているクラスを示します。
|
int |
lookupModes()
このルックアップ・オブジェクトがどのアクセス保護クラスのメンバーを生成できるかを示します。
|
MethodHandleInfo |
revealDirect(MethodHandle target)
この参照オブジェクトまたは類似のオブジェクトによって作成された直接メソッド・ハンドルを解読します。
|
String |
toString()
ルックアップの実行元となるクラスの名前を表示します。
|
MethodHandle |
unreflect(Method m)
ルックアップ・クラスがアクセス権を持つ場合に、mへの直接メソッド・ハンドルを作成します。
|
MethodHandle |
unreflectConstructor(Constructor<?> c)
リフレクトされたコンストラクタのメソッド・ハンドルを生成します。
|
MethodHandle |
unreflectGetter(Field f)
リフレクトされたフィールドに対する読取りアクセスを提供するメソッド・ハンドルを生成します。
|
MethodHandle |
unreflectSetter(Field f)
リフレクトされたフィールドに対する書込みアクセスを提供するメソッド・ハンドルを生成します。
|
MethodHandle |
unreflectSpecial(Method m, Class<?> specialCaller)
リフレクトされたメソッドのメソッド・ハンドルを生成します。
|
public static final int PUBLIC
public static final int PRIVATE
public static final int PROTECTED
public static final int PACKAGE
packageアクセス(デフォルト・アクセス)を表す単一ビット・マスク(lookupModesの結果に寄与する可能性がある)。値は0x08ですが、これは、特定のどの修飾子ビットにも、意味のあるかたちでは対応しません。public Class<?> lookupClass()
このクラスは最大レベルのアクセス権を暗黙的に示しますが、非publicメンバーにアクセスできるかどうかを制御するビット・マスクlookupModesにより、アクセス権がさらに制限される可能性もあります。
public int lookupModes()
呼出し元のクラス上で新しく作成されたルックアップ・オブジェクトでは、存在するすべてのビットが設定されますが、これは、呼出し元のクラスは自身のすべてのメンバーにアクセスできるからです。以前のルックアップ・オブジェクトから作成された新しいルックアップ・クラス上のルックアップ・オブジェクトでは、いくつかのモード・ビットがゼロに設定されている可能性があります。その目的は、新しいルックアップ・オブジェクト経由でのアクセスを制限し、元のルックアップ・オブジェクトと新しいルックアップ・クラスの両方から到達可能な名前だけにアクセスできるようにすることです。
public MethodHandles.Lookup in(Class<?> requestedLookupClass)
lookupClassとして報告します。
ただし、結果となるLookupオブジェクトは、元のオブジェクトと同等以下のアクセス機能しか持たないことが保証されます。具体的には、アクセス機能が次のように失われる可能性があります。
requestedLookupClass - 新しいルックアップ・オブジェクト用に要求されるルックアップ・クラスNullPointerException - 引数がnullの場合public String toString()
Class.getNameから報告されるものです。)このルックアップに許可されるアクセスに制限がある場合、そのことを示すために、スラッシュとキーワードで構成される接尾辞がクラス名に追加されます。キーワードは許可される最強のアクセスを表しており、次のように選択されます。
MethodHandles.lookupから取得されたオブジェクトの場合だけです。Lookup.inで作成されたオブジェクトでは常にアクセスが制限され、接尾辞が表示されます。
(protectedアクセスがprivateアクセスより強いというのは、奇妙に感じるかもしれません。packageアクセスから独立して見た場合、protectedアクセスが最初に失われるものになりますが、それは、呼出し元と呼出し先の間に直接的なサブクラス関係が必要になるからです。)
toString 、クラス: Objectin(java.lang.Class<?>)public MethodHandle findStatic(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException
findVirtualやfindSpecialのように、メソッド・ハンドルの型に追加のレシーバ引数が挿入されることはありません。)このメソッドとそのすべての引数型にルックアップ・オブジェクトからアクセスできる必要があります。
返されるメソッド・ハンドルの引数が可変引数になるのは、メソッドの可変引数修飾子ビット(0x0080)が設定されている場合だけです。
返されるメソッド・ハンドルが呼び出された場合、そのメソッドのクラスは初期化されます(まだ初期化されていない場合)。
例:
import static java.lang.invoke.MethodHandles.*; import static java.lang.invoke.MethodType.*; ... MethodHandle MH_asList = publicLookup().findStatic(Arrays.class, "asList", methodType(List.class, Object[].class)); assertEquals("[x, y]", MH_asList.invoke("x", "y").toString());
refc - メソッドのアクセス元となるクラスname - メソッドの名前type - メソッドの型NoSuchMethodException - メソッドが存在しない場合IllegalAccessException - アクセス・チェックが失敗した場合、またはメソッドがstaticでない場合、あるいはメソッドの可変引数修飾子ビットが設定されてasVarargsCollectorが失敗した場合SecurityException - セキュリティ・マネージャが存在し、それがアクセスを拒否した場合NullPointerException - いずれかの引数がnullの場合public MethodHandle findVirtual(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException
refc)を追加したものになります。このメソッドとそのすべての引数型にルックアップ・オブジェクトからアクセスできる必要があります。
ハンドルは呼出し時に、最初の引数をレシーバとみなし、そのレシーバの型でディスパッチしてどのメソッド実装に入るかを決定します。(このディスパッチ・アクションは、invokevirtualまたはinvokeinterface命令によって実行されるアクションと同一です。)
ルックアップ・クラスがメンバーにアクセスする完全な権限を持っている場合、最初の引数の型はrefcになります。それ以外の場合、メンバーはprotectedである必要があり、最初の引数の型はルックアップ・クラスに制限されます。
返されるメソッド・ハンドルの引数が可変引数になるのは、メソッドの可変引数修飾子ビット(0x0080)が設定されている場合だけです。
invokevirtual命令とfindVirtualによって生成されるメソッド・ハンドルとの間の一般的な等価性のために、クラスがMethodHandleで名前文字列がinvokeExactまたはinvokeの場合は、結果のメソッド・ハンドルは同じtype引数のMethodHandles.exactInvokerまたはMethodHandles.invokerで生成されたものと同等です。例:
import static java.lang.invoke.MethodHandles.*; import static java.lang.invoke.MethodType.*; ... MethodHandle MH_concat = publicLookup().findVirtual(String.class, "concat", methodType(String.class, String.class)); MethodHandle MH_hashCode = publicLookup().findVirtual(Object.class, "hashCode", methodType(int.class)); MethodHandle MH_hashCode_String = publicLookup().findVirtual(String.class, "hashCode", methodType(int.class)); assertEquals("xy", (String) MH_concat.invokeExact("x", "y")); assertEquals("xy".hashCode(), (int) MH_hashCode.invokeExact((Object)"xy")); assertEquals("xy".hashCode(), (int) MH_hashCode_String.invokeExact("xy")); // interface method: MethodHandle MH_subSequence = publicLookup().findVirtual(CharSequence.class, "subSequence", methodType(CharSequence.class, int.class, int.class)); assertEquals("def", MH_subSequence.invoke("abcdefghi", 3, 6).toString()); // constructor "internal method" must be accessed differently: MethodType MT_newString = methodType(void.class); //()V for new String() try { assertEquals("impossible", lookup() .findVirtual(String.class, "<init>", MT_newString)); } catch (NoSuchMethodException ex) { } // OK MethodHandle MH_newString = publicLookup() .findConstructor(String.class, MT_newString); assertEquals("", (String) MH_newString.invokeExact());
refc - メソッドのアクセス元となるクラスまたはインタフェースname - メソッドの名前type - メソッドの型(レシーバ引数は含まない)NoSuchMethodException - メソッドが存在しない場合IllegalAccessException - アクセス・チェックが失敗した場合、またはメソッドがstaticの場合、あるいはメソッドの可変引数修飾子ビットが設定されてasVarargsCollectorが失敗した場合SecurityException - セキュリティ・マネージャが存在し、それがアクセスを拒否した場合NullPointerException - いずれかの引数がnullの場合public MethodHandle findConstructor(Class<?> refc, MethodType type) throws NoSuchMethodException, IllegalAccessException
要求する型の戻り値の型はvoidでなければいけません。(これは、JVMのコンストラクタ型記述子の扱いに準拠しています。)
返されるメソッド・ハンドルの引数が可変引数になるのは、コンストラクタの可変引数修飾子ビット(0x0080)が設定されている場合だけです。
返されるメソッド・ハンドルが呼び出された場合、そのコンストラクタのクラスは初期化されます(まだ初期化されていない場合)。
例:
import static java.lang.invoke.MethodHandles.*; import static java.lang.invoke.MethodType.*; ... MethodHandle MH_newArrayList = publicLookup().findConstructor( ArrayList.class, methodType(void.class, Collection.class)); Collection orig = Arrays.asList("x", "y"); Collection copy = (ArrayList) MH_newArrayList.invokeExact(orig); assert(orig != copy); assertEquals(orig, copy); // a variable-arity constructor: MethodHandle MH_newProcessBuilder = publicLookup().findConstructor( ProcessBuilder.class, methodType(void.class, String[].class)); ProcessBuilder pb = (ProcessBuilder) MH_newProcessBuilder.invoke("x", "y", "z"); assertEquals("[x, y, z]", pb.command().toString());
refc - メソッドのアクセス元となるクラスまたはインタフェースtype - メソッドの型(レシーバ引数は含めず、戻り値の型はvoidにする)NoSuchMethodException - コンストラクタが存在しない場合IllegalAccessException - アクセス・チェックが失敗した場合、またはメソッドの可変引数修飾子ビットが設定されていてasVarargsCollectorが失敗した場合SecurityException - セキュリティ・マネージャが存在し、それがアクセスを拒否した場合NullPointerException - いずれかの引数がnullの場合public MethodHandle findSpecial(Class<?> refc, String name, MethodType type, Class<?> specialCaller) throws NoSuchMethodException, IllegalAccessException
specialCaller内のinvokespecial命令から呼び出されたかのように、レシーバでのメソッドのオーバーライド・チェックをバイパスします。メソッド・ハンドルの型はメソッドのもので、適切に制限されたレシーバ型が追加されます。(レシーバ型はspecialCallerまたはサブタイプです。)このメソッドとそのすべての引数型にルックアップ・オブジェクトからアクセスできる必要があります。
メソッド解決の前に、明示的に指定された呼出し元クラスがルックアップ・クラスと同一でない場合、またはこのルックアップ・オブジェクトがprivateアクセス特権を持たない場合は、アクセスが失敗します。
返されるメソッド・ハンドルの引数が可変引数になるのは、メソッドの可変引数修飾子ビット(0x0080)が設定されている場合だけです。
(注: "<init>"というJVM内部メソッドは、特殊な環境ではinvokespecial命令はそれらを参照できますが、このAPIでは可視ではありません。安全な方法でインスタンス初期化メソッドにアクセスするには、findConstructorを使用してください。)
例:
import static java.lang.invoke.MethodHandles.*; import static java.lang.invoke.MethodType.*; ... static class Listie extends ArrayList { public String toString() { return "[wee Listie]"; } static Lookup lookup() { return MethodHandles.lookup(); } } ... // no access to constructor via invokeSpecial: MethodHandle MH_newListie = Listie.lookup() .findConstructor(Listie.class, methodType(void.class)); Listie l = (Listie) MH_newListie.invokeExact(); try { assertEquals("impossible", Listie.lookup().findSpecial( Listie.class, "<init>", methodType(void.class), Listie.class)); } catch (NoSuchMethodException ex) { } // OK // access to super and self methods via invokeSpecial: MethodHandle MH_super = Listie.lookup().findSpecial( ArrayList.class, "toString" , methodType(String.class), Listie.class); MethodHandle MH_this = Listie.lookup().findSpecial( Listie.class, "toString" , methodType(String.class), Listie.class); MethodHandle MH_duper = Listie.lookup().findSpecial( Object.class, "toString" , methodType(String.class), Listie.class); assertEquals("[]", (String) MH_super.invokeExact(l)); assertEquals(""+l, (String) MH_this.invokeExact(l)); assertEquals("[]", (String) MH_duper.invokeExact(l)); // ArrayList method try { assertEquals("inaccessible", Listie.lookup().findSpecial( String.class, "toString", methodType(String.class), Listie.class)); } catch (IllegalAccessException ex) { } // OK Listie subl = new Listie() { public String toString() { return "[subclass]"; } }; assertEquals(""+l, (String) MH_this.invokeExact(subl)); // Listie method
refc - メソッドのアクセス元となるクラスまたはインタフェースname - メソッドの名前(「<init>」であってはいけない)type - メソッドの型(レシーバ引数は含まない)specialCaller - invokespecialを実行する、提案された呼出し元クラスNoSuchMethodException - メソッドが存在しない場合IllegalAccessException - アクセス・チェックが失敗した場合、またはメソッドの可変引数修飾子ビットが設定されていてasVarargsCollectorが失敗した場合SecurityException - セキュリティ・マネージャが存在し、それがアクセスを拒否した場合NullPointerException - いずれかの引数がnullの場合public MethodHandle findGetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException
refc - メソッドのアクセス元となるクラスまたはインタフェースname - フィールドの名前type - フィールドの型NoSuchFieldException - フィールドが存在しない場合IllegalAccessException - アクセス・チェックが失敗した場合、またはフィールドがstaticの場合SecurityException - セキュリティ・マネージャが存在し、それがアクセスを拒否した場合NullPointerException - いずれかの引数がnullの場合public MethodHandle findSetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException
refc - メソッドのアクセス元となるクラスまたはインタフェースname - フィールドの名前type - フィールドの型NoSuchFieldException - フィールドが存在しない場合IllegalAccessException - アクセス・チェックが失敗した場合、またはフィールドがstaticの場合SecurityException - セキュリティ・マネージャが存在し、それがアクセスを拒否した場合NullPointerException - いずれかの引数がnullの場合public MethodHandle findStaticGetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException
返されるメソッド・ハンドルが呼び出された場合、そのフィールドのクラスは初期化されます(まだ初期化されていない場合)。
refc - メソッドのアクセス元となるクラスまたはインタフェースname - フィールドの名前type - フィールドの型NoSuchFieldException - フィールドが存在しない場合IllegalAccessException - アクセス・チェックが失敗した場合、またはフィールドがstaticでない場合SecurityException - セキュリティ・マネージャが存在し、それがアクセスを拒否した場合NullPointerException - いずれかの引数がnullの場合public MethodHandle findStaticSetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException
返されるメソッド・ハンドルが呼び出された場合、そのフィールドのクラスは初期化されます(まだ初期化されていない場合)。
refc - メソッドのアクセス元となるクラスまたはインタフェースname - フィールドの名前type - フィールドの型NoSuchFieldException - フィールドが存在しない場合IllegalAccessException - アクセス・チェックが失敗した場合、またはフィールドがstaticでない場合SecurityException - セキュリティ・マネージャが存在し、それがアクセスを拒否した場合NullPointerException - いずれかの引数がnullの場合public MethodHandle bind(Object receiver, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException
defc内では、指定された名前と型を持つメソッドにルックアップ・クラスからアクセスできる必要があります。このメソッドとそのすべての引数型にルックアップ・オブジェクトからアクセスできる必要があります。メソッド・ハンドルの型はメソッドの型であり、追加のレシーバ・パラメータは一切挿入されません。指定されたレシーバがメソッド・ハンドルにバインドされるため、メソッド・ハンドルが呼び出されるたびに要求されたメソッドが指定されたレシーバ上で呼び出されるようになります。
返されるメソッド・ハンドルの引数が可変引数になるのは、メソッドの可変引数修飾子ビット(0x0080)が設定されており、かつ、末尾の配列引数が唯一の引数でない場合だけです。(末尾の配列引数が唯一の引数である場合、指定されたレシーバの値はその引数にバインドされます。)
これは次のコードと等価です。
ここで、import static java.lang.invoke.MethodHandles.*; import static java.lang.invoke.MethodType.*; ... MethodHandle mh0 = lookup().findVirtual(defc, name, type); MethodHandle mh1 = mh0.bindTo(receiver); MethodType mt1 = mh1.type(); if (mh0.isVarargsCollector()) mh1 = mh1.asVarargsCollector(mt1.parameterType(mt1.parameterCount()-1)); return mh1;
defcはreceiver.getClass()またはそのクラスのスーパー・タイプのいずれかであり、その中では、要求されたメソッドにルックアップ・クラスからアクセスできます。(bindToは可変引数を保持しません。)receiver - メソッドのアクセス元となるオブジェクトname - メソッドの名前type - メソッドの型(レシーバ引数は含まない)NoSuchMethodException - メソッドが存在しない場合IllegalAccessException - アクセス・チェックが失敗した場合、またはメソッドの可変引数修飾子ビットが設定されていてasVarargsCollectorが失敗した場合SecurityException - セキュリティ・マネージャが存在し、それがアクセスを拒否した場合NullPointerException - いずれかの引数がnullの場合MethodHandle.bindTo(java.lang.Object)、findVirtual(java.lang.Class<?>, java.lang.String, java.lang.invoke.MethodType)public MethodHandle unreflect(Method m) throws IllegalAccessException
accessibleフラグが設定されていない場合、ルックアップ・クラスに代わって即座にアクセス・チェックが実行されます。mがpublicでない場合、結果となるハンドルを信頼できない相手と共有しないでください。
返されるメソッド・ハンドルの引数が可変引数になるのは、メソッドの可変引数修飾子ビット(0x0080)が設定されている場合だけです。
mがstaticで、返されるメソッド・ハンドルが呼び出された場合、そのメソッドのクラスは初期化されます(まだ初期化されていない場合)。
m - リフレクトされたメソッドIllegalAccessException - アクセス・チェックが失敗した場合、またはメソッドの可変引数修飾子ビットが設定されていてasVarargsCollectorが失敗した場合NullPointerException - 引数がnullの場合public MethodHandle unreflectSpecial(Method m, Class<?> specialCaller) throws IllegalAccessException
specialCaller内のinvokespecial命令から呼び出されたかのように、レシーバでのメソッドのオーバーライド・チェックをバイパスします。メソッド・ハンドルの型はメソッドのもので、適切に制限されたレシーバ型が追加されます。(レシーバ型はspecialCallerまたはサブタイプです。)メソッドのaccessibleフラグが設定されていない場合、invokespecial命令がリンクされていたかのように、ルックアップ・クラスに代わって即座にアクセス・チェックが実行されます。
メソッド解決の前に、明示的に指定された呼出し元クラスがルックアップ・クラスと同一でない場合、またはこのルックアップ・オブジェクトがprivateアクセス特権を持たない場合は、アクセスが失敗します。
返されるメソッド・ハンドルの引数が可変引数になるのは、メソッドの可変引数修飾子ビット(0x0080)が設定されている場合だけです。
m - リフレクトされたメソッドspecialCaller - メソッドを形式上呼び出すクラスIllegalAccessException - アクセス・チェックが失敗した場合、またはメソッドの可変引数修飾子ビットが設定されていてasVarargsCollectorが失敗した場合NullPointerException - いずれかの引数がnullの場合public MethodHandle unreflectConstructor(Constructor<?> c) throws IllegalAccessException
newInstance操作を実行し、メソッド・ハンドルに渡された引数に基づいてコンストラクタのクラスの新しいインスタンスを作成します。
コンストラクタのaccessibleフラグが設定されていない場合、ルックアップ・クラスに代わって即座にアクセス・チェックが実行されます。
返されるメソッド・ハンドルの引数が可変引数になるのは、コンストラクタの可変引数修飾子ビット(0x0080)が設定されている場合だけです。
返されるメソッド・ハンドルが呼び出された場合、そのコンストラクタのクラスは初期化されます(まだ初期化されていない場合)。
c - リフレクトされたコンストラクタIllegalAccessException - アクセス・チェックが失敗した場合、またはメソッドの可変引数修飾子ビットが設定されていてasVarargsCollectorが失敗した場合NullPointerException - 引数がnullの場合public MethodHandle unreflectGetter(Field f) throws IllegalAccessException
accessibleフラグが設定されていない場合、ルックアップ・クラスに代わって即座にアクセス・チェックが実行されます。
フィールドがstaticで、返されるメソッド・ハンドルが呼び出された場合、そのフィールドのクラスは初期化されます(まだ初期化されていない場合)。
f - リフレクトされたフィールドIllegalAccessException - アクセス・チェックが失敗した場合NullPointerException - 引数がnullの場合public MethodHandle unreflectSetter(Field f) throws IllegalAccessException
accessibleフラグが設定されていない場合、ルックアップ・クラスに代わって即座にアクセス・チェックが実行されます。
フィールドがstaticで、返されるメソッド・ハンドルが呼び出された場合、そのフィールドのクラスは初期化されます(まだ初期化されていない場合)。
f - リフレクトされたフィールドIllegalAccessException - アクセス・チェックが失敗した場合NullPointerException - 引数がnullの場合public MethodHandleInfo revealDirect(MethodHandle target)
target - シンボリック参照コンポーネントに解決する直接メソッド・ハンドルSecurityException - セキュリティ・マネージャが存在し、それがアクセスを拒否した場合IllegalArgumentException - ターゲットが直接メソッド・ハンドルでない場合、またはアクセス・チェックが失敗した場合NullPointerException - ターゲットがnullの場合MethodHandleInfo バグまたは機能を送信
詳細なAPIリファレンスおよび開発者ドキュメントについては、Java SEのドキュメントを参照してください。そのドキュメントには、概念的な概要、用語の定義、回避方法、有効なコード例などの、開発者を対象にしたより詳細な説明が含まれています。
Copyright© 1993, 2014, Oracle and/or its affiliates. All rights reserved.