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) |
static FT f; | (T) 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.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
、クラス: Object
in(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.