- すべての実装されたインタフェース:
Constable
メソッド・ハンドルの内容
メソッド・ハンドルは、そのパラメータと戻り値の型に従って動的にかつ強く型付けされています。 これらは、ベースとなるメソッドの名前や定義元のクラスで識別されません。 メソッド・ハンドルは、そのハンドル自身の型記述子に一致するシンボリック型記述子を使って呼び出す必要があります。
どのメソッド・ハンドルもtype
アクセサ経由で型記述子を報告します。 この型記述子はMethodType
オブジェクトであり、その構造は一連のクラスになっていますが、その1つがメソッドの戻り値の型です(存在しない場合はvoid.class
)。
メソッド・ハンドルの型によって、受け入れる呼出しのタイプや適用される変換の種類が制御されます。
メソッド・ハンドルには、invokeExact
およびinvoke
と呼ばれる特殊なインボーカ・メソッドのペアが含まれています。 どちらのインボーカ・メソッドも、メソッド・ハンドルのベースとなるメソッド、コンストラクタ、フィールド、またはその他の操作(引数や戻り値の変換によって変更されたもの)に対する直接アクセスを提供します。 どちらのインボーカも、メソッド・ハンドル自身の型に厳密に一致する呼出しを受け入れます。 厳密でないプレーンなインボーカは、その他のさまざまな呼出しタイプも受け入れます。
メソッド・ハンドルは不変であり、可視の状態を一切持ちません。 当然、状態を公開しているベースとなるメソッドやデータにそれらをバインドすることは可能です。 Javaメモリー・モデルに関しては、どのメソッド・ハンドルもその(内部)フィールドがすべてfinal変数であるかのように振る舞います。 これは、アプリケーションから可視状態になったメソッド・ハンドルはすべて、常に完全な形式になっていることを意味します。 これは、メソッド・ハンドルがデータ競合時の共有変数を通じて公開された場合でも言えることです。
ユーザーによるメソッド・ハンドルのサブクラス化はできません。 実装はMethodHandle
の内部サブクラスを作成することもしないこともありますが、これはObject.getClass
操作を使って確認できます。 メソッド・ハンドルのクラス階層(存在する場合)は、時期や各種ベンダーの実装ごとに変わる可能性があるため、プログラマは、あるメソッド・ハンドルに関する結論をその特定のクラスから引き出すべきではありません。
メソッド・ハンドルのコンパイル
invokeExact
またはinvoke
の名前を含むJavaメソッド呼出し式は、Javaソース・コードからメソッド・ハンドルを呼び出すことができます。 ソース・コードの視点から見ると、これらのメソッドは任意の引数を取ることができ、その結果を任意の戻り値の型にキャストできます。 これは形式上、インボーカ・メソッドに戻り値の型Object
と可変引数のObject
引数を与えることで実現されていますが、これらのメソッドにはシグネチャ・ポリモーフィズムと呼ばれる追加の特性が備わっており、これにより、この自由な形式の呼出しがJVM実行スタックに直接接続されます。
仮想メソッドでは普通のことですが、invokeExact
およびinvoke
に対するソース・レベルの呼出しは、invokevirtual
命令にコンパイルされます。 通常とは異なる点として、コンパイラは実際の引数の型を記録する必要があり、引数に対するメソッド呼出し変換を実行することができません。 その代わりに、スタック上にそれ自身の変換されていない型に従ってスタックをプッシュする命令を生成する必要があります。 メソッド・ハンドル・オブジェクト自体は、スタック上の引数の前にプッシュされます。 次にコンパイラは、引数と戻り値の型を記述するシンボリック型記述子でメソッド・ハンドルを呼び出すinvokevirtual
命令を生成します。
完全なシンボリック型記述子を発行するには、コンパイラは戻り値の型も決定する必要があります。 これは、メソッド呼び出し式があればキャストに基づいています。呼び出しが式の場合はObject
、呼び出しが文の場合はvoid
になります。 キャスト先はプリミティブ型でもかまいません(ただしvoid
は不可)。
特殊なケースとして、キャストされていないnull
引数にはjava.lang.Void
のシンボリック型記述子が与えられます。 Void
型の参照はnull参照以外には存在しないため、型Void
のあいまいさが問題になることはありません。
メソッド・ハンドルの呼出し
invokevirtual
命令が初めて実行されるとき、命令内の名前を記号的に解決し、メソッド呼び出しが静的に合法であることを確認することによってリンクされます。 これは、invokeExact
とinvoke
への呼び出しにも適用されます。 この場合、コンパイラによって発行されたシンボリック型記述子は、正しい構文がチェックされ、その中に含まれる名前が解決されます。 したがって、シンボリック型記述子が文法的に正しい形式であり、型が存在するかぎり、メソッド・ハンドルを呼び出すinvokevirtual
命令は常にリンクされます。
invokevirtual
がリンク後に実行される際には、まずJVMによってレシーバ・メソッド・ハンドルの型がチェックされ、それがシンボリック型記述子に一致することが確認されます。 型一致が失敗した場合、それは、呼出し元が呼び出そうとしているメソッドが、呼出し対象の個別のメソッド・ハンドル上には存在しないことを意味します。
invokeExact
の場合、(シンボリック型名を解決したあとの)呼出しの型記述子が、レシーバ・メソッド・ハンドルのメソッド型と厳密に一致する必要があります。 厳密でないプレーンなinvoke
の場合、解決済みの型記述子がレシーバのasType
メソッドの有効な引数でなければいけません。 したがって、プレーンなinvoke
の方が、invokeExact
よりも許容範囲が広くなります。
型一致処理のあと、invokeExact
の呼出しにより、メソッド・ハンドルのベースとなるメソッド(または場合によってはほかの動作)が直接的かつ即時に呼び出されます。
呼出し元によって指定されたシンボリック型記述子がメソッド・ハンドル自身の型に厳密に一致する場合、プレーンなinvoke
の呼出しはinvokeExact
の呼び出しと同じように動作します。 型の不一致が存在する場合、invoke
は、asType
を呼び出したかのようにレシーバ・メソッド・ハンドルの型の調整を試み、厳密な呼出しが可能なメソッド・ハンドルM2
を取得します。 これにより、呼出し元と呼出し先の間での、メソッドの型に関するより強力なネゴシエーションが可能となります。
(ノート: 調整後のメソッド・ハンドルM2
を直接監視することはできないため、実装においてその実体化を行う必要はありません。)
呼出しのチェック
典型的なプログラムでは、メソッド・ハンドルの型一致処理は通常成功します。 しかし、一致が失敗した場合は、JVMからWrongMethodTypeException
が直接的に(invokeExact
の場合)またはasType
の呼出しが失敗した場合のように間接的に(invoke
の場合)スローされます。
したがって、静的に型付けされたプログラムではリンケージ・エラーとして示されるメソッド型の不一致が、メソッド・ハンドルを使用するプログラムでは動的なWrongMethodTypeException
として示される可能性があります。
メソッド型には"live" Class
オブジェクトが含まれているため、メソッド型の一致は型名とクラス・ローダーの両方を考慮に入れます。 したがって、メソッド・ハンドルM
が、あるクラス・ローダーL1
で作成され、別のL2
で使用される場合でも、L2
で解決された呼出し元のシンボリック型記述子の一致処理は、L1
で解決された元の呼出し先メソッドのシンボリック型記述子に対して行われるため、メソッド・ハンドル呼出しは型保証されます。 L1
での解決は、M
が作成されてその型が割り当てられるときに行われるのに対し、L2
での解決は、invokevirtual
命令のリンク時に行われます。
型記述子のチェックとは別に、その基本メソッドを呼び出すメソッド・ハンドル機能は制限されていません。 ある非publicメソッドのメソッド・ハンドルがそのメソッドへのアクセスを持つクラスによって作成された場合、結果となるハンドルは、そのハンドルへの参照を受け取った任意の呼出し元によって任意の場所で使用できます。
リフレクション・メソッドが呼び出されるたびにアクセス・チェックが行われるCore Reflection APIと異なり、メソッド・ハンドルのアクセス・チェックはメソッド・ハンドルの作成時に実行されます。 ldc
(以下を参照)の場合は、定数メソッド・ハンドルのベースとなる定数プール・エントリのリンク処理の一部として、アクセス・チェックが実行されます。
したがって、非publicメソッドへのハンドルや非publicクラス内のメソッドへのハンドルは、一般に非公開にしておくべきです。 信頼できないコードがそれらを使用しても問題が発生しない場合を除き、それらを信頼できないコードに渡さないようにしてください。
メソッド・ハンドルの作成
Javaコードは、そのコードからアクセス可能な任意のメソッド、コンストラクタ、またはフィールドに直接アクセスするメソッド・ハンドルを作成できます。 これは、MethodHandles.Lookup
と呼ばれる機能に基づいた反射的なAPIを介して行われます。 たとえば、静的メソッド・ハンドルはLookup.findStatic
から取得できます。 Core Reflection APIオブジェクトからの変換メソッド(Lookup.unreflect
など)もあります。
アクセス可能なフィールド、メソッド、およびコンストラクタに対応するメソッド・ハンドルはクラスや文字列の場合と同じく、クラス・ファイルの定数プール内で直接、ldc
バイト・コードによってロードされる定数として表現することもできます。 新しいタイプの定数プール・エントリCONSTANT_MethodHandle
は、関連するCONSTANT_Methodref
、CONSTANT_InterfaceMethodref
、またはCONSTANT_Fieldref
定数プール・エントリを直接参照します。 (メソッド・ハンドル定数の詳細は、Java Virtual Machine仕様の4.4.8および5.4.3.5の項を参照してください。)
可変アリティ修飾子ビット(0x0080
)を持つメソッドまたはコンストラクタからのルックアップまたは定数ロードによって生成されたメソッド・ハンドルは、asVarargsCollector
またはwithVarargs
の助けを借りて定義されているかのように、対応する可変アリティを持ちます。
メソッド参照は静的メソッド、静的でないメソッドのいずれかを参照できます。 静的でない場合、メソッド・ハンドルの型には、ほかのすべての引数の前に追加された明示的なレシーバ引数が含まれます。 メソッド・ハンドルの型に含まれる最初のレシーバ引数の型は、メソッドが最初に要求されたクラスに従って決定されます。 (たとえば、ldc
経由で取得された静的でないメソッド・ハンドルの場合、レシーバの型は、定数プール・エントリで指定されたクラスになります。)
メソッド・ハンドル定数は、それに対応するバイト・コード命令と同じリンク時アクセス・チェックの対象であり、ldc
命令によって対応するリンケージ・エラーがスローされます(バイト・コードの動作でそのようなエラーがスローされる場合)。
この結果として、protectedメンバーへのアクセスはアクセスするクラスかそのいずれかのサブクラスのレシーバのみに制限され、アクセスするクラスは次にはprotectedメンバーの定義クラスのサブクラス(パッケージの兄弟)になる必要があります。 メソッド参照が現在のパッケージ外部にあるクラスの静的でないprotectedメソッドまたはフィールドを参照する場合、レシーバ引数はアクセスするクラスの型にナロー変換されます。
仮想メソッドへのメソッド・ハンドルを呼び出した場合、メソッドのルックアップは常にレシーバ(つまり最初の引数)内で行われます。
特定の仮想メソッド実装への非仮想メソッド・ハンドルも作成できます。 これらは、レシーバの型に基づく仮想ルックアップを実行しません。 そのようなメソッド・ハンドルは、同じメソッドに対するinvokespecial
命令の効果をシミュレートします。 また、非仮想メソッド・ハンドルを作成して、プライベート・メソッド(適用可能)にあるinvokevirtual
またはinvokeinterface
イン・スト・ラク・ションの影響をシミュレートすることもできます。
使用例
次にいくつかの使用例を示します。Object x, y; String s; int i; MethodType mt; MethodHandle mh; MethodHandles.Lookup lookup = MethodHandles.lookup(); // mt is (char,char)String mt = MethodType.methodType(String.class, char.class, char.class); mh = lookup.findVirtual(String.class, "replace", mt); s = (String) mh.invokeExact("daddy",'d','n'); // invokeExact(Ljava/lang/String;CC)Ljava/lang/String; assertEquals(s, "nanny"); // weakly typed invocation (using MHs.invoke) s = (String) mh.invokeWithArguments("sappy", 'p', 'v'); assertEquals(s, "savvy"); // mt is (Object[])List mt = MethodType.methodType(java.util.List.class, Object[].class); mh = lookup.findStatic(java.util.Arrays.class, "asList", mt); assert(mh.isVarargsCollector()); x = mh.invoke("one", "two"); // invoke(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object; assertEquals(x, java.util.Arrays.asList("one","two")); // mt is (Object,Object,Object)Object mt = MethodType.genericMethodType(3); mh = mh.asType(mt); x = mh.invokeExact((Object)1, (Object)2, (Object)3); // invokeExact(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; assertEquals(x, java.util.Arrays.asList(1,2,3)); // mt is ()int mt = MethodType.methodType(int.class); mh = lookup.findVirtual(java.util.List.class, "size", mt); i = (int) mh.invokeExact(java.util.Arrays.asList(1,2,3)); // invokeExact(Ljava/util/List;)I assert(i == 3); mt = MethodType.methodType(void.class, String.class); mh = lookup.findVirtual(java.io.PrintStream.class, "println", mt); mh.invokeExact(System.out, "Hello, world."); // invokeExact(Ljava/io/PrintStream;Ljava/lang/String;)V
invokeExact
またはプレーンなinvoke
に対する上記の各呼出しは、直後のコメントに示しているシンボリック型記述子を持つ単一のinvokevirtual命令を生成します。 これらの例で、ヘルパー・メソッドassertEquals
は、その引数でObjects.equals
を呼び出し、その結果がtrueであることを表明するメソッドであると想定されています。
例外
メソッドinvokeExact
とinvoke
はThrowable
をスローするように宣言されていますが、これは、メソッド・ハンドルからスローできるものについて、静的な制限は一切ないことを示すためです。 JVMはチェック例外と非チェック例外を区別しない(もちろん、それらのクラスによる区別は行う)ため、チェック例外をメソッド・ハンドル呼出しに帰することの、バイト・コードの構造への影響は特に存在しません。 しかし、Javaソース・コードでは、メソッド・ハンドル呼出しを実行するメソッドは、Throwable
を明示的にスローするか、あるいはすべてのスロー可能オブジェクトをローカルでキャッチし、コンテキスト内で正しいもののみをスローし直し、不正なものはラップする必要があります。
シグネチャ・ポリモーフィズム
invokeExact
とプレーンなinvoke
の通常とは異なるコンパイル動作やリンク動作には、シグネチャ・ポリモーフィズムという用語が使用されます。 Java言語仕様で定義されているように、シグネチャ・ポリモーフィズム・メソッドとは、広範な呼出しシグネチャや戻り値の型のいずれでも動作できるメソッドのことです。
ソース・コードに含まれるシグネチャ・ポリモーフィズム・メソッドの呼出しは、要求されたシンボリック型記述子がどのようなものであってもコンパイルされます。 Javaコンパイラは通常どおり、指定されたシンボリック型記述子を持つ、指定されたメソッドに対するinvokevirtual
命令を出力します。 通常と異なる部分は、シンボリック型記述子が、メソッド宣言からではなく実際の引数と戻り値の型から派生される点です。
シグネチャ・ポリモーフィズム呼出しを含むバイト・コードがJVMで処理されるとき、そのような呼出しはすべて、シンボリック型記述子がどのようなものであっても正常にリンクされます。 (ほかの場所で説明したように、JVMは型保証を保持するため、そのような呼出しを適切な動的型チェックで保護します。)
バイトコード・ジェネレータ(コンパイラのバックエンドも含む)は、それらのメソッドに対する変換されていないシンボリック型記述子を出力する必要があります。 シンボリック・リンケージを決定するツールは、そのような変換されていない記述子をリンケージ・エラーを報告しないで受け入れる必要があります。
メソッド・ハンドルとCore Reflection API間の相互運用
Lookup
APIのファクトリ・メソッドを使えば、Core Reflection APIオブジェクトとして表現された任意のクラス・メンバーを、同等の動作を備えたメソッド・ハンドルに変換できます。 たとえば、リフレクションのMethod
をメソッド・ハンドルに変換するには、Lookup.unreflect
を使用します。 結果となるメソッド・ハンドルは一般に、ベースとなるクラス・メンバーへのより直接的かつ効率的なアクセス機能を提供します。
特殊な場合として、このクラス内のシグネチャ・ポリモーフィズム・メソッドであるinvokeExact
またはプレーンなinvoke
をCore Reflection APIを使って確認した場合、それらは通常の非ポリモーフィズム・メソッドとして示されます。 Class.getDeclaredMethod
で示されるそれらのリフレクション表現は、このAPIでの特殊なステータスの影響を受けません。 たとえば、Method.getModifiers
では、同様に宣言されたすべてのメソッドで必要になる修飾子ビット(この場合はnative
ビットやvarargs
ビットなど)が厳密に報告されます。
これらのメソッド(をリフレクトしたもの)は、ほかのリフレクトされたメソッドと同じく、java.lang.reflect.Method.invoke
を使って呼び出すことができます。 ただし、そのようなリフレクション呼出しを行っても、メソッド・ハンドルは呼び出されません。 そのような呼出しに必要な引数(Object[]
型の単一の引数)を渡してもその引数は無視され、UnsupportedOperationException
がスローされます。
invokevirtual
命令は、メソッド・ハンドルを任意のシンボリック型記述子の下でネイティブに呼び出すことができるため、このリフレクション表示は、バイト・コード経由でのこれらのメソッドの通常の表示と矛盾します。 したがって、これら2つのネイティブ・メソッドをClass.getDeclaredMethod
でリフレクション表示したものは、プレースホルダー専用とみなすことができます。
特定の型記述子のインボーカ・メソッドを取得するには、MethodHandles.exactInvoker
またはMethodHandles.invoker
を使用します。 Lookup.findVirtual
APIも、指定された任意の型記述子に対する、invokeExact
またはプレーンなinvoke
を呼び出すメソッド・ハンドルを返すことができます。
メソッド・ハンドルとJavaジェネリックス間の相互運用
Javaジェネリック型を使って宣言されたメソッド、コンストラクタ、またはフィールドのメソッド・ハンドルを取得できます。 コア・リフレクションAPIと同様に、メソッド・ハンドルのタイプはソース・レベル・タイプの紀元から構築されます。 メソッド・ハンドルを呼び出す際には、その引数の型や戻り値のキャストの型がジェネリックの型または型インスタンスになる可能性があります。 これが起こった場合、コンパイラは、invokevirtual
命令のシンボリック型記述子を構築する際に、それらの型を対応するイレイジャで置き換えます。
メソッド・ハンドルの関数形式の型は、Javaのパラメータ化された型(ジェネリック型)を使って表現されませんが、これは、関数形式の型とパラメータ化されたJava型との間に3つの不一致が存在しているからです。
- メソッド型は、引数なしから許可される引数の最大数まで、さまざまな引数カウントが可能です。 ジェネリックスは可変個引数でないため、これを表現することができません。
- メソッド型にはプリミティブ型の引数を指定できますが、Javaジェネリック型はプリミティブ型に対応できません。
- メソッド・ハンドルに対する高階関数(コンビネータ)は通常、複数の引数長を持つ関数型など、広範な関数型にわたって汎用的です。 そのようなジェネリック特性をJavaの型パラメータで表現することは不可能です。
引数の制限
JVMは、あらゆる種類のすべてのメソッドおよびコンストラクタに対して、255個のスタックされた引数という絶対制限を適用します。 この制限は、一部のクラスでより制限的になることがあります。long
またはdouble
引数は、(引数カウント制限のために) 2つの引数スロットとしてカウントします。- 非staticメソッドは、メソッドが呼び出されるオブジェクトの余分な引数を消費します。
- コンストラクタは、構築されるオブジェクトの余分な引数を消費します。
- メソッド・ハンドルの
invoke
メソッド(または他のシグネチャ・ポリモーフィズム・メソッド)は、非仮想であるため、非仮想レシーバ・オブジェクト以外にメソッド・ハンドル自体の余分な引数を消費します。
IllegalArgumentException
がスローされます。 特に、メソッド・ハンドルの型は、正確に最大255個の引数カウントを持つことはできません。 - 導入されたバージョン:
- 1.7
- 関連項目:
-
メソッドのサマリー
修飾子と型メソッド説明asCollector
(int collectArgPos, Class<?> arrayType, int arrayLength) array-collectingメソッドのハンドルを作成します。ハンドルは、与えられた位置から開始して与えられた数の位置引数を受け取り、それらを配列引数に集めます。asCollector
(Class<?> arrayType, int arrayLength) 末尾の指定された数の定位置引数を受け取り、それらを集めて1つの配列引数に格納するような、配列収集メソッド・ハンドルを作成します。固定引数カウント・メソッド・ハンドル(その他の点では現在のメソッド・ハンドルと同等のもの)を作成します。asSpreader
(int spreadArgPos, Class<?> arrayType, int arrayLength) array-spreadingメソッド・ハンドルを作成します。ハンドルは、指定された位置に配列引数を受け取り、配列の代わりに位置引数としてその要素を展開します。asSpreader
(Class<?> arrayType, int arrayLength) 末尾の1つの配列引数を受け取り、その要素を複数の定位置引数に分配するような、配列分配メソッド・ハンドルを作成します。final MethodHandle
asType
(MethodType newType) 現在のメソッド・ハンドルの型を新しい型に適応させるアダプタ・メソッド・ハンドルを生成します。asVarargsCollector
(Class<?> arrayType) 末尾の任意の数の定位置引数を受け取り、それらを集めて1つの配列引数に格納できるような、可変引数アダプタを作成します。値x
をメソッド・ハンドルの最初の引数にバインドしますが、その呼出しは行いません。このインスタンスの名目記述子を返します(作成可能な場合)、作成できない場合は空のOptional
を返します。final Object
メソッド・ハンドルを呼び出しますが、その際、呼出し元のどのような型記述子でも許可され、必要に応じて引数や戻り値の変換も実行されます。final Object
invokeExact
(Object... args) メソッド・ハンドルを呼び出し、その際、呼出し元のどのような型記述子でも許可されますが、型は厳密に一致する必要があります。invokeWithArguments
(Object... arguments) 可変アリティ呼び出しを実行し、指定された配列の引数をメソッド・ハンドルに渡します。これは、Object
型のみを記述する引数サイトの不正確なinvoke
を経由するかのように、実際の引数の数は引数の配列の長さです。invokeWithArguments
(List<?> arguments) 可変アリティ呼び出しを実行し、指定されたリストの引数をメソッド・ハンドルに渡します。これは、Object
型のみを記述する不正なinvoke
を経由するかのように、実際の引数の数は引数リストの長さです。boolean
このメソッド・ハンドルが可変引数呼出しをサポートしているかどうかを判定します。toString()
メソッド・ハンドルの文字列表現を返しますが、これは、文字列"MethodHandle"
で始まり、メソッド・ハンドルの型の文字列表現で終わります。type()
このメソッド・ハンドルの型を報告します。withVarargs
(boolean makeVarargs)
-
メソッドの詳細
-
type
public MethodType type()このメソッド・ハンドルの型を報告します。invokeExact
によるこのメソッド・ハンドルのすべての呼出しは、この型と厳密に一致する必要があります。- 戻り値:
- メソッド・ハンドルの型
-
invokeExact
メソッド・ハンドルを呼び出し、その際、呼出し元のどのような型記述子でも許可されますが、型は厳密に一致する必要があります。invokeExact
のコール・サイトのシンボリック型記述子は、このメソッド・ハンドルの型
と厳密に一致する必要があります。 引数や戻り値の変換は許可されません。Core Reflection API経由で監視した場合、このメソッドは、1つのオブジェクト配列を取って1つのオブジェクトを返す単一のネイティブ・メソッドのように見えます。 このネイティブ・メソッドが、
java.lang.reflect.Method.invoke
経由で直接的に呼び出されたり、JNI経由で呼び出されたり、あるいはLookup.unreflect
経由で間接的に呼び出されたりすると、メソッドからUnsupportedOperationException
がスローされます。- パラメータ:
args
- 可変引数で静的に表現された、シグネチャ・ポリモーフィズム・パラメータ・リスト- 戻り値:
Object
で静的に表現された、シグネチャ・ポリモーフィズム結果- 例外:
WrongMethodTypeException
- ターゲットの型が呼出し元のシンボリック型記述子と同一でない場合Throwable
- 配下のメソッドからスローされたものがすべて、そのまま変更されずにメソッド・ハンドル呼出し経由で伝播される
-
invoke
メソッド・ハンドルを呼び出しますが、その際、呼出し元のどのような型記述子でも許可され、必要に応じて引数や戻り値の変換も実行されます。コール・サイトのシンボリック型記述子がこのメソッド・ハンドルの
型
と厳密に一致する場合、invokeExact
の場合と同様に呼出しが進みます。それ以外の場合の呼出しの進行は、まずこのメソッド・ハンドルを調整するために
asType
を呼び出してこのメソッド・ハンドルを必要な型に調整し、その後、調整済みのメソッド・ハンドルでinvokeExact
を使用した場合と同様になります。asType
呼出しが実際に行われるという保証はありません。 JVMが呼出し結果を予測できる場合、JVMは呼出し元の引数に対して直接調整を実行し、ターゲット・メソッド・ハンドルをその厳密な型に従って呼び出す可能性があります。invoke
のコール・サイトの解決済み型記述子は、レシーバのasType
メソッドの有効な引数でなければいけません。 特に、呼出し先が可変引数コレクタでない場合、呼出し元は呼出し先の型と同じ引数長を指定する必要があります。Core Reflection API経由で監視した場合、このメソッドは、1つのオブジェクト配列を取って1つのオブジェクトを返す単一のネイティブ・メソッドのように見えます。 このネイティブ・メソッドが、
java.lang.reflect.Method.invoke
経由で直接的に呼び出されたり、JNI経由で呼び出されたり、あるいはLookup.unreflect
経由で間接的に呼び出されたりすると、メソッドからUnsupportedOperationException
がスローされます。- パラメータ:
args
- 可変引数で静的に表現された、シグネチャ・ポリモーフィズム・パラメータ・リスト- 戻り値:
Object
で静的に表現された、シグネチャ・ポリモーフィズム結果- 例外:
WrongMethodTypeException
- ターゲットの型を呼出し元のシンボリック型記述子に適合させることができない場合ClassCastException
- ターゲットの型を呼出し元に適合させることは可能であるが、参照キャストが失敗した場合Throwable
- 配下のメソッドからスローされたものがすべて、そのまま変更されずにメソッド・ハンドル呼出し経由で伝播される
-
invokeWithArguments
可変アリティ呼び出しを実行し、指定された配列の引数をメソッド・ハンドルに渡します。これは、Object
型のみを記述する引数サイトの不正確なinvoke
を経由するかのように、実際の引数の数は引数の配列の長さです。具体的には次のステップのように実行が進みますが、JVMがメソッド呼出しの効果を予測できる場合はメソッドが呼び出される保証はありません。
- 引数配列の長さ(
N
)を決定します。 null参照の場合、N=0
になります。 - 配列の
N
要素を論理引数リストとして収集し、各引数は静的にObject
と入力します。 M
として、このメソッド・ハンドルのタイプのパラメータ・カウントを判別します。N
引数またはM
引数の一般的なタイプTN
をN
より小さい場合はTN=MethodType.genericMethodType(Math.min(N, M))
として決定します。N
がM
より大きい場合は、次のチェックとアクションを実行して、論理引数リストを短縮します:- このメソッド・ハンドルが、配列型
A[]
の「後続パラメータ」で可変アリティを持っていることを確認してください。 そうでない場合は、WrongMethodTypeException
で失敗します。 - 後続の要素(それらの
N-M+1
があります)を論理引数リストからA[]
型の単一の配列に集め、asType
変換を使用して、後続の各引数をA
型に変換します。 - これらの変換のいずれかが不可能であると判明した場合、後続の要素が
A
にキャストできない場合はClassCastException
で、後続要素がnull
でA
が参照型でない場合はNullPointerException
で失敗します。 - 収集された論理的な引数を
A[]
型の配列に置き換え、配列の長さを短縮してM
の長さにします。 この最後の引数は、静的型A[]
を保持します。 N
のパラメータ・タイプをObject
からA[]
に変更して、TN
のタイプを調整します。
- このメソッド・ハンドルが、配列型
- 元のターゲット・メソッド・ハンドル
MH0
を必要な型に強制的に変更します(MH1 = MH0.asType(TN)
)。 - 引数リストを
N
の別々の引数A0, ...
に広げます。 - 型を調整したメソッド・ハンドルを、展開した引数で呼び出します: MH1.invokeExact(A0, ...)。
- 戻り値を
Object
参照として取得します。
ターゲット・メソッド・ハンドルに可変アリティがあり、引数リストがそのアリティよりも長い場合、末尾の配列引数の位置から始まる余分な引数は、適切な型の配列に(
asType
コンバージョンの場合と同じように、可能な場合)で集められ、呼び出しは続けられます短縮された引数リスト。 このように、254以上のスロットに広がる「ジャンボ引数リスト」は依然として一様に処理することができます。invokeWithArguments
に渡された元の配列がターゲットへの直接の引数として受け入れられていたとしても、この引数alwaysは、配列引数を"recycle"に渡して直接ターゲット・メソッドに渡すことができるgeneric
呼び出しモードとは異なり、新しい配列パラメータを作成します方法。 実際の引数の番号M
がアリティN
であり、最後の引数が動的にA[]
型の適切な配列であっても、コール・サイトが静的に引数をObject
とタイプするので、新しい1要素配列にボクシングされます。配列型ではありません。 これは特別なルールではなく、「可変アリティのための規則」の通常の効果です。asType
ステップのアクションのために、次の引数変換が必要に応じて適用されます。- 参照キャスト
- アンボクシング
- プリミティブ・ワイドニング変換
- 可変アリティ変換
呼び出しから返された結果は、プリミティブの場合はボクシングされ、戻り値の型がvoidの場合は強制的にnullにされます。
invokeWithArguments
はシグネチャ・ポリモーフィズム・メソッドのinvokeExact
やinvoke
とは異なり、Core Reflection APIやJNI経由で通常どおりにアクセスできます。 したがってこれは、ネイティブ・コードやリフレクション・コードとメソッド・ハンドルとのブリッジとして使用できます。- APIのノート:
- この呼び出しは、次のコードとほぼ同じです:
// for jumbo argument lists, adapt varargs explicitly: int N = (arguments == null? 0: arguments.length); int M = this.type.parameterCount(); int MAX_SAFE = 127; // 127 longs require 254 slots, which is OK if (N > MAX_SAFE && N > M && this.isVarargsCollector()) { Class<?> arrayType = this.type().lastParameterType(); Class<?> elemType = arrayType.getComponentType(); if (elemType != null) { Object args2 = Array.newInstance(elemType, M); MethodHandle arraySetter = MethodHandles.arrayElementSetter(arrayType); for (int i = 0; i < M; i++) { arraySetter.invoke(args2, i, arguments[M-1 + i]); } arguments = Arrays.copyOf(arguments, M); arguments[M-1] = args2; return this.asFixedArity().invokeWithArguments(arguments); } } // done with explicit varargs processing // Handle fixed arity and non-jumbo variable arity invocation. MethodHandle invoker = MethodHandles.spreadInvoker(this.type(), 0); Object result = invoker.invokeExact(this, arguments);
- パラメータ:
arguments
- ターゲットに渡す引数- 戻り値:
- ターゲットから返された結果
- 例外:
ClassCastException
- 引数が参照キャストで変換できない場合WrongMethodTypeException
- 指定された数のObject
引数を取るようにターゲットの型を調整できない場合Throwable
- ターゲット・メソッド呼出しでスローされたすべてのオブジェクト- 関連項目:
- 引数配列の長さ(
-
invokeWithArguments
可変アリティ呼び出しを実行し、指定されたリストの引数をメソッド・ハンドルに渡します。これは、Object
型のみを記述する不正なinvoke
を経由するかのように、実際の引数の数は引数リストの長さです。このメソッドは次のコードとも同等です。
invokeWithArguments(arguments.toArray())
このメソッド・ハンドルが可変アリティを持つ場合、ジャンボ・サイズのリストは許容されます。 詳細については、
invokeWithArguments(Object[])
を参照してください。- パラメータ:
arguments
- ターゲットに渡す引数- 戻り値:
- ターゲットから返された結果
- 例外:
NullPointerException
-arguments
がnull参照の場合ClassCastException
- 引数が参照キャストで変換できない場合WrongMethodTypeException
- 指定された数のObject
引数を取るようにターゲットの型を調整できない場合Throwable
- ターゲット・メソッド呼出しでスローされたすべてのオブジェクト
-
asType
public final MethodHandle asType(MethodType newType) 現在のメソッド・ハンドルの型を新しい型に適応させるアダプタ・メソッド・ハンドルを生成します。 結果のメソッド・ハンドルが目的の新しい型と等しい型を報告することが、保証されます。元の型と新しい型が等しい場合は
this
を返します。新しいメソッド・ハンドルを呼び出すと、次のステップが実行されます。
- 入力引数リストを変換し、元のメソッド・ハンドルの引数リストに一致させます。
- 変換後の引数リストで元のメソッド・ハンドルを呼び出します。
- 元のメソッド・ハンドルから返された結果を、新しいメソッド・ハンドルの戻り値の型に変換します。
このメソッドのために、
invokeExact
と厳密でないプレーンなinvoke
との間で動作が大きく異なります。 2つのメソッドは、呼び出し元の型記述子が呼び出し先と正確に一致する場合と同じステップを実行しますが、型が異なる場合、呼び出し元と呼び出し先の型を一致させるために、asType
(または内部相当物)も呼び出します。現在のメソッドが可変引数メソッド・ハンドルの場合、別の場所で説明しているように、引数リストの変換時にいくつかの引数の1つの配列内への変換と収集が行われる可能性があります。 その他のすべての場合には、あらゆる変換はペアで適用され、それぞれの引数または戻り値は厳密に1つの引数または戻り値(または戻り値なし)に変換されます。 適用された変換は、古いメソッド・ハンドル・タイプと新しいメソッド・ハンドル・タイプの対応するコンポーネント・タイプを調べることによって定義されます。
T0とT1を、対応する新しいパラメータの型と古いパラメータの型、あるいは古い戻り値の型と新しい戻り値の型とします。 具体的には、ある有効なインデックス
i
について、T0=newType.parameterType(i)
、T1=this.type().parameterType(i)
とします。 また、戻り値については、T0=this.type().returnType()
、T1=newType.returnType()
とします。 型が同じである場合、新しいメソッド・ハンドルは、対応する引数または戻り値(存在する場合)に対して何の変更も加えません。 それ以外の場合、可能であれば次のいずれかの変換が適用されます。- T0とT1が参照の場合、T1へのキャストが適用されます。 (型同士が何らかの形で関連している必要はありません。 これは、nullの動的な値はどのような参照型にも変換できるからです。)
- T0とT1がプリミティブの場合、Javaメソッド呼出し変換(JLS 5.3)が適用されます(存在する場合)。 (具体的には、T0をT1に変換するには、プリミティブ・ワイドニング変換を行う必要があります。)
- T0がプリミティブでT1が参照の場合、Javaキャスト変換(JLS 5.5)が適用されます(存在する場合)。 (具体的には、値がT0からそのラッパー・クラスにボクシングされたあと、必要に応じてT1にワイドニングされます。)
- T0が参照でT1がプリミティブの場合、実行時にアンボクシング変換が適用されますが、そのあと、プリミティブ値に対してJavaメソッド呼出し変換(JLS 5.3)が行われる可能性があります。 (これらはプリミティブ・ワイドニング変換です。) T0はラッパー・クラスであるか、ラッパー・クラスのスーパー・タイプである必要があります。 (T0がObjectの場合、これらは、
java.lang.reflect.Method.invoke
によって許可される変換になります。) アンボクシング変換は成功する可能性が存在する必要があり、T0自体がラッパー・クラスでない場合は、T0のサブタイプの中に、アンボクシング後のプリミティブ値がT1にワイドニング可能であるようなラッパー・クラスTWが少なくとも1つ存在する必要があります。 - 戻り値の型T1がvoidとしてマークされている場合、返される値はすべて破棄されます
- 戻り値の型T0がvoidでT1が参照の場合、値nullが導入されます。
- 戻り値の型T0がvoidでT1がプリミティブの場合、値ゼロが導入されます。
必要なペア単位の変換のいずれかを行えない場合は、メソッド・ハンドルの変換を行えません。
参照の引数や戻り値に適用される変換では、実行時に、失敗する可能性のある追加の実行時チェックが必要になる可能性があります。 アンボクシング操作は、元の参照がnullであるために失敗する可能性があり、
NullPointerException
が発行されます。 不正な型のオブジェクトへの参照の場合にもアンボクシング操作や参照キャストが失敗する可能性があり、ClassCastException
が発行されます。 アンボクシング操作は何種類かのラッパーを受け入れることができますが、使用可能なものが1つもなければ、ClassCastException
がスローされます。- パラメータ:
newType
- 新しいメソッド・ハンドルの期待される型- 戻り値:
- 必要な引数変換をすべて実行したあとで
this
に委譲し、必要なあらゆる戻り値変換の手配も行うメソッド・ハンドル - 例外:
NullPointerException
-newType
がnull参照の場合WrongMethodTypeException
- 変換できない場合- 関連項目:
-
asSpreader
public MethodHandle asSpreader(Class<?> arrayType, int arrayLength) 末尾の1つの配列引数を受け取り、その要素を複数の定位置引数に分配するような、配列分配メソッド・ハンドルを作成します。 新しいメソッド・ハンドルはそのターゲットとして、現在のメソッド・ハンドルを適応させます。 アダプタの型はターゲットの型と基本的に同じになりますが、ターゲットの型の最後のarrayLength
個のパラメータが単一のarrayType
型の配列パラメータで置き換えられる点が異なります。配列要素の型が元のターゲットの対応するいずれかの引数の型と異なる場合、
asType
が呼び出されたかのようにして、元のターゲットが配列要素を直接取るように適応されます。このアダプタを呼び出すと、末尾の配列引数がその配列の各要素で置き換えられ、各要素がそれぞれターゲットに対する独立した引数になります。 (引数の順序は維持されます。) それらは、ターゲットの末尾の各パラメータの型へのキャストやアンボクシングが行われて、ペアで変換されます。 最後にターゲットが呼び出されます。 ターゲットから最終的に返されたものが、そのまま変更されずにアダプタから返されます。
アダプタはターゲットを呼び出す前に、配列にちょうど十分な数の要素が含まれていて、ターゲット・メソッド・ハンドルに正しい数の引数を提供できることを確認します。 (必要な要素の数がゼロの場合は、配列をnullにしてもかまいません。)
アダプタが呼び出されると、指定された
array
引数の長さが、array.length
またはarraylength
バイトコードのように照会されます。 アダプタがゼロ長の後続配列引き数を受け入れる場合、指定されたarray
引き数は長さゼロの配列かnull
のどちらかです。それ以外の場合、配列はnull
の場合はNullPointerException
をスローし、配列の要素数が正しい場合はIllegalArgumentException
をスローします。配列分配メソッド・ハンドルの単純な例を、いくつか次に示します。
MethodHandle equals = publicLookup() .findVirtual(String.class, "equals", methodType(boolean.class, Object.class)); assert( (boolean) equals.invokeExact("me", (Object)"me")); assert(!(boolean) equals.invokeExact("me", (Object)"thee")); // spread both arguments from a 2-array: MethodHandle eq2 = equals.asSpreader(Object[].class, 2); assert( (boolean) eq2.invokeExact(new Object[]{ "me", "me" })); assert(!(boolean) eq2.invokeExact(new Object[]{ "me", "thee" })); // try to spread from anything but a 2-array: for (int n = 0; n <= 10; n++) { Object[] badArityArgs = (n == 2 ? new Object[0] : new Object[n]); try { assert((boolean) eq2.invokeExact(badArityArgs) && false); } catch (IllegalArgumentException ex) { } // OK } // spread both arguments from a String array: MethodHandle eq2s = equals.asSpreader(String[].class, 2); assert( (boolean) eq2s.invokeExact(new String[]{ "me", "me" })); assert(!(boolean) eq2s.invokeExact(new String[]{ "me", "thee" })); // spread second arguments from a 1-array: MethodHandle eq1 = equals.asSpreader(Object[].class, 1); assert( (boolean) eq1.invokeExact("me", new Object[]{ "me" })); assert(!(boolean) eq1.invokeExact("me", new Object[]{ "thee" })); // spread no arguments from a 0-array or null: MethodHandle eq0 = equals.asSpreader(Object[].class, 0); assert( (boolean) eq0.invokeExact("me", (Object)"me", new Object[0])); assert(!(boolean) eq0.invokeExact("me", (Object)"thee", (Object[])null)); // asSpreader and asCollector are approximate inverses: for (int n = 0; n <= 2; n++) { for (Class<?> a : new Class<?>[]{Object[].class, String[].class, CharSequence[].class}) { MethodHandle equals2 = equals.asSpreader(a, n).asCollector(a, n); assert( (boolean) equals2.invokeWithArguments("me", "me")); assert(!(boolean) equals2.invokeWithArguments("me", "thee")); } } MethodHandle caToString = publicLookup() .findStatic(Arrays.class, "toString", methodType(String.class, char[].class)); assertEquals("[A, B, C]", (String) caToString.invokeExact("ABC".toCharArray())); MethodHandle caString3 = caToString.asCollector(char[].class, 3); assertEquals("[A, B, C]", (String) caString3.invokeExact('A', 'B', 'C')); MethodHandle caToString2 = caString3.asSpreader(char[].class, 2); assertEquals("[A, B, C]", (String) caToString2.invokeExact('A', "BC".toCharArray()));
- パラメータ:
arrayType
- 通常はObject[]
で、分配する引数の抽出元となる配列引数の型arrayLength
- 入力配列引数から分配する引数の数- 戻り値:
- 最後の配列引数を分配してから元のメソッド・ハンドルを呼び出す新しいメソッド・ハンドル
- 例外:
NullPointerException
-arrayType
がnull参照の場合IllegalArgumentException
-arrayType
が配列型でない場合、ターゲットが少なくともarrayLength
パラメータ型を持たない場合、arrayLength
が負の場合、または結果のメソッド・ハンドルの型のパラメータ数が多すぎる場合WrongMethodTypeException
- 暗黙的なasType
呼出しが失敗した場合- 関連項目:
-
asSpreader
public MethodHandle asSpreader(int spreadArgPos, Class<?> arrayType, int arrayLength) array-spreadingメソッド・ハンドルを作成します。ハンドルは、指定された位置に配列引数を受け取り、配列の代わりに位置引数としてその要素を展開します。 新しいメソッド・ハンドルはそのターゲットとして、現在のメソッド・ハンドルを適応させます。 ターゲット型のarrayLength
パラメータが、ゼロ・ベースの位置spreadArgPos
から始まり、型arrayType
の単一の配列パラメータに置き換えられる点を除いて、アダプタの型はターゲットの型と同じになります。このメソッドは
asSpreader(Class, int)
と非常によく似た動作をしますが、パラメータ・リスト内のどの位置にスプレッドを行うべきかを示すspreadArgPos
引数を追加します。- APIのノート:
- 例:
MethodHandle compare = LOOKUP.findStatic(Objects.class, "compare", methodType(int.class, Object.class, Object.class, Comparator.class)); MethodHandle compare2FromArray = compare.asSpreader(0, Object[].class, 2); Object[] ints = new Object[]{3, 9, 7, 7}; Comparator<Integer> cmp = (a, b) -> a - b; assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 0, 2), cmp) < 0); assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 1, 3), cmp) > 0); assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 2, 4), cmp) == 0);
- パラメータ:
spreadArgPos
- 展開が開始される引数リスト内の位置(ゼロ・ベースのインデックス)。arrayType
- 通常はObject[]
で、分配する引数の抽出元となる配列引数の型arrayLength
- 入力配列引数から分配する引数の数- 戻り値:
- 元のメソッド・ハンドルを呼び出す前に、指定された位置に配列引数を渡す新しいメソッド・ハンドル
- 例外:
NullPointerException
-arrayType
がnull参照の場合IllegalArgumentException
-arrayType
は配列型ではない場合、または標的は、少なくともarrayLength
パラメータ型を有するか、またはしない場合arrayLength
が負である場合、またはspreadArgPos
が不正値(負の値、または引数の数を超えるarrayLengthと一緒に)を有する場合、または得られたメソッド・ハンドル・タイプは「あまりにも多くのパラメータ」を持っているかどうWrongMethodTypeException
- 暗黙的なasType
呼出しが失敗した場合- 導入されたバージョン:
- 9
- 関連項目:
-
withVarargs
public MethodHandle withVarargs(boolean makeVarargs) booleanフラグがtrueの場合は「可変アリティ」、そうでない場合は「固定アリティ」になるようにこのメソッド・ハンドルを修正します。 メソッド・ハンドルが既に適切なアリティ・モードになっている場合、メソッド・ハンドルは変更されずにそのまま返されます。- APIのノート:
このメソッドは、元のハンドルがあった場合にのみ、結果のアダプタも可変アリティであることを保証するために、可変アリティの可能性のあるメソッド・ハンドルを適合させる場合に便利なことがあります。 たとえば、このコードは、可変アリティ・プロパティを妨げずにハンドル
mh
の最初の引数をint
に変更:mh.asType(mh.type().changeParameterType(0,int.class)) .withVarargs(mh.isVarargsCollector())
この呼び出しは、次のコードとほぼ同じです:
if (makeVarargs == isVarargsCollector()) return this; else if (makeVarargs) return asVarargsCollector(type().lastParameterType()); else return asFixedArity();
- パラメータ:
makeVarargs
- 戻り値のメソッド・ハンドルが可変アリティ動作を持つ必要がある場合はtrue- 戻り値:
- 場合によっては調整された可変アリティの振る舞いを持つ同じ型のメソッド・ハンドル
- 例外:
IllegalArgumentException
-makeVarargs
がtrueで、このメソッド・ハンドルに後続の配列パラメータがない場合- 導入されたバージョン:
- 9
- 関連項目:
-
asCollector
public MethodHandle asCollector(Class<?> arrayType, int arrayLength) 末尾の指定された数の定位置引数を受け取り、それらを集めて1つの配列引数に格納するような、配列収集メソッド・ハンドルを作成します。 新しいメソッド・ハンドルはそのターゲットとして、現在のメソッド・ハンドルを適応させます。 アダプタの型はターゲットの型と基本的に同じになりますが、末尾の単一のパラメータ(通常はarrayType
型)がarrayLength
個のパラメータ(型はarrayType
の要素の型)で置き換えられる点が異なります。配列型が元のターゲットの最後の引数の型と異なる場合、
asType
が呼び出されたかのようにして、元のターゲットがその配列型を直接取るように適応されます。このアダプタを呼び出すと、末尾の
arrayLength
個の引数が単一の新しいarrayType
型配列で置き換えられますが、その配列の要素は、置き換えられた引数を(順番に)並べたものになります。 最後にターゲットが呼び出されます。 ターゲットから最終的に返されたものが、そのまま変更されずにアダプタから返されます。(
arrayLength
がゼロの場合は、配列が共有定数になる可能性もあります。)(ノート:
arrayType
は、しばしば元のターゲットの最後のパラメータ・タイプと同じです。 これが明示的な引数になっているのは、asSpreader
との対称性を維持するためですが、ターゲットの最後のパラメータの型として単純なObject
を使用できるようにするためでもあります。)特定の数の収集された引数に限定されない収集アダプタを作成するには、代わりに
asVarargsCollector
またはwithVarargs
を使用します。配列収集メソッド・ハンドルの例を、いくつか次に示します。
MethodHandle deepToString = publicLookup() .findStatic(Arrays.class, "deepToString", methodType(String.class, Object[].class)); assertEquals("[won]", (String) deepToString.invokeExact(new Object[]{"won"})); MethodHandle ts1 = deepToString.asCollector(Object[].class, 1); assertEquals(methodType(String.class, Object.class), ts1.type()); //assertEquals("[won]", (String) ts1.invokeExact( new Object[]{"won"})); //FAIL assertEquals("[[won]]", (String) ts1.invokeExact((Object) new Object[]{"won"})); // arrayType can be a subtype of Object[] MethodHandle ts2 = deepToString.asCollector(String[].class, 2); assertEquals(methodType(String.class, String.class, String.class), ts2.type()); assertEquals("[two, too]", (String) ts2.invokeExact("two", "too")); MethodHandle ts0 = deepToString.asCollector(Object[].class, 0); assertEquals("[]", (String) ts0.invokeExact()); // collectors can be nested, Lisp-style MethodHandle ts22 = deepToString.asCollector(Object[].class, 3).asCollector(String[].class, 2); assertEquals("[A, B, [C, D]]", ((String) ts22.invokeExact((Object)'A', (Object)"B", "C", "D"))); // arrayType can be any primitive array type MethodHandle bytesToString = publicLookup() .findStatic(Arrays.class, "toString", methodType(String.class, byte[].class)) .asCollector(byte[].class, 3); assertEquals("[1, 2, 3]", (String) bytesToString.invokeExact((byte)1, (byte)2, (byte)3)); MethodHandle longsToString = publicLookup() .findStatic(Arrays.class, "toString", methodType(String.class, long[].class)) .asCollector(long[].class, 1); assertEquals("[123]", (String) longsToString.invokeExact((long)123));
ノート: たとえ元のターゲット・メソッド・ハンドルがあったとしても、得られるアダプタは決して可変メソッド・ハンドルではありません。
- パラメータ:
arrayType
- 通常はObject[]
で、複数の引数を集める配列引数の型arrayLength
- 新しい配列引数内に集める引数の数- 戻り値:
- いくつかの末尾の引数を1つの配列内に集めてから元のメソッド・ハンドルを呼び出す新しいメソッド・ハンドル
- 例外:
NullPointerException
-arrayType
がnull参照の場合IllegalArgumentException
-arrayType
が配列型でない、またはarrayType
がこのメソッド・ハンドルの末尾のパラメータ型に代入できない、またはarrayLength
が正しい配列サイズでない、または結果のメソッド・ハンドルの型のパラメータ数が多すぎる場合WrongMethodTypeException
- 暗黙的なasType
呼出しが失敗した場合- 関連項目:
-
asCollector
public MethodHandle asCollector(int collectArgPos, Class<?> arrayType, int arrayLength) array-collectingメソッドのハンドルを作成します。ハンドルは、与えられた位置から開始して与えられた数の位置引数を受け取り、それらを配列引数に集めます。 新しいメソッド・ハンドルはそのターゲットとして、現在のメソッド・ハンドルを適応させます。 アダプタの型は、ターゲットの型と同じですが、collectArgPos
(通常、型arrayType
の)で示される位置のパラメータは、arrayType
の要素型であるarrayLength
パラメータに置き換えられます。このメソッドは
asCollector(Class, int)
と非常によく似た動作をしますが、collectArgPos
引数は引数リストのどの位置に引数を集めるべきかを示します。 この索引はゼロ・ベースです。- APIのノート:
- 例
StringWriter swr = new StringWriter(); MethodHandle swWrite = LOOKUP.findVirtual(StringWriter.class, "write", methodType(void.class, char[].class, int.class, int.class)).bindTo(swr); MethodHandle swWrite4 = swWrite.asCollector(0, char[].class, 4); swWrite4.invoke('A', 'B', 'C', 'D', 1, 2); assertEquals("BC", swr.toString()); swWrite4.invoke('P', 'Q', 'R', 'S', 0, 4); assertEquals("BCPQRS", swr.toString()); swWrite4.invoke('W', 'X', 'Y', 'Z', 3, 1); assertEquals("BCPQRSZ", swr.toString());
ノート: たとえ元のターゲット・メソッド・ハンドルがあったとしても、得られるアダプタは決して可変メソッド・ハンドルではありません。
- パラメータ:
collectArgPos
- 収集を開始するパラメータ・リスト内のゼロ・ベースの位置。arrayType
- 通常はObject[]
で、複数の引数を集める配列引数の型arrayLength
- 新しい配列引数内に集める引数の数- 戻り値:
- 元のメソッド・ハンドルを呼び出す前に、いくつかの引数を配列に集める新しいメソッド・ハンドル
- 例外:
NullPointerException
-arrayType
がnull参照の場合IllegalArgumentException
-arrayType
でない場合、アレイ・タイプまたはarrayType
このメソッド・ハンドル配列パラメータ・タイプに割り当て可能でない、またはarrayLength
法的配列サイズではない、またはcollectArgPos
不正値(負の数、または引数の数より大きい)を有している、または得られる方法ハンドル・タイプが「あまりにも多くのパラメータ」を有するであろうWrongMethodTypeException
- 暗黙的なasType
呼出しが失敗した場合- 導入されたバージョン:
- 9
- 関連項目:
-
asVarargsCollector
public MethodHandle asVarargsCollector(Class<?> arrayType) 末尾の任意の数の定位置引数を受け取り、それらを集めて1つの配列引数に格納できるような、可変引数アダプタを作成します。アダプタの型や動作はターゲットの型や動作と基本的に同じになりますが、特定の
invoke
要求やasType
要求によって、末尾の複数の定位置引数がターゲットの末尾のパラメータ内に集められる可能性がある点が異なります。 また、ターゲットの最後のパラメータ・タイプが異なる場合でも、アダプタの「最後のパラメータ・タイプ」はarrayType
になります。メソッド・ハンドルがすでに可変引数であり、その末尾のパラメータの型が
arrayType
と同じである場合、この変換によってthis
が返される可能性があります。invokeExact
でアダプタを呼び出した場合、ターゲットの呼出し時に引数は一切変更されません。 (ノート: この動作は固定引数コレクタとは異なりますが、それは、これが固定長の引数ではなく不定長の配列全体を受け入れるからです。)厳密でないプレーンな
invoke
でアダプタを呼び出したときに呼出し元の型がアダプタと同じ場合、invokeExact
の場合と同様にターゲットが呼び出されます。 (これは、型が一致する場合のinvoke
の通常の動作です。)それ以外の場合、呼出し元とアダプタの引数長が同じで、呼出し元の末尾のパラメータの型がアダプタの末尾のパラメータの型と同じかその型に代入可能な参照型である場合、固定引数のメソッド・ハンドルで
asType
を使用する場合と同様に、引数と戻り値がペアで変換されます。それ以外の場合は、引数長が異なるか、アダプタの末尾のパラメータの型が、呼出し元の対応する型から代入可能ではありません。 この場合、アダプタは、元の末尾の引数位置から先にあるすべての末尾の引数を新しい1つの
arrayType
型配列で置き換えますが、その配列の要素は、置き換えられた引数を(順番に)並べたものになります。後続の配列引数の前に位置引数のターゲット要件を満たすために、呼び出し元タイプは、少なくとも十分な引数と正しい型の引数を提供する必要があります。 したがって、呼出し元は最低限、
N-1
個の引数(N
はターゲットの引数長)を提供する必要があります。 さらに、入力引数からターゲットの引数への変換が存在する必要があります。 プレーンなinvoke
のその他の使用法と同じく、これらの基本的な要件が満たされない場合はWrongMethodTypeException
がスローされる可能性があります。すべての場合で、ターゲットから最終的に返されたものが、そのまま変更されずにアダプタから返されます。
最後の場合は、ターゲット・メソッド・ハンドルが一時的に、呼出し元の型で要求される引数長に固定引数コレクタで適応される場合とまったく同じです。 (
asCollector
の場合と同様に、配列の長さがゼロの場合は、新しい配列の代わりに共有定数が使用される可能性があります。 暗黙的なasCollector
呼出しでIllegalArgumentException
またはWrongMethodTypeException
がスローされた場合、可変引数アダプタの呼び出しからWrongMethodTypeException
がスローされる必要があります。)また、
asType
の動作も可変引数アダプタ向けに特殊化されており、その結果、厳密でないプレーンなinvoke
が常に、asType
を呼び出してターゲットの型を調整したあとでinvokeExact
を呼び出すのと同等であるという不変性が維持されています。 したがって、可変引数アダプタがasType
要求への応答として固定引数コレクタを構築するのは、アダプタと要求された型の、引数長または末尾の引数の型が異なる場合だけです。 結果となる固定引数コレクタの型は、(必要であれば)asType
がふたたび適用されたかのようにして、ペア単位の変換によって、要求された型にさらに調整されます。メソッド・ハンドルが
CONSTANT_MethodHandle
定数のldc
命令を実行して取得されたものであり、そのターゲット・メソッドが(修飾子ビット0x0080
で)可変引数メソッドとしてマークされている場合、そのメソッド・ハンドル定数がasVarargsCollector
を呼び出して作成されたかのように、そのメソッド・ハンドルは複数の引数長を受け入れます。事前に決められた数の引数を収集し、その数を反映した型を持つ収集アダプタを作成するには、代わりに
asCollector
を使用してください。どのメソッド・ハンドル変換も、ドキュメントに明記されていないかぎり、可変引数の新しいメソッド・ハンドルを生成することはありません。 したがって、
asVarargsCollector
およびwithVarargs
のほかに、MethodHandle
およびMethodHandles
のすべてのメソッドは、元のオペランド(例えば、メソッド・ハンドル自身の型のasType
)を返すように指定されている場合を除いて、固定アリティ付きメソッド・ハンドルを返します。すでに可変引数であるメソッド・ハンドル上で
asVarargsCollector
を呼び出すと、同じ型と動作を備えたメソッド・ハンドルが生成されます。 その戻り値は、元の可変引数メソッド・ハンドルであることも、そうでないこともあります。リストを作成する可変引数メソッド・ハンドルの例を、次に示します。
MethodHandle deepToString = publicLookup() .findStatic(Arrays.class, "deepToString", methodType(String.class, Object[].class)); MethodHandle ts1 = deepToString.asVarargsCollector(Object[].class); assertEquals("[won]", (String) ts1.invokeExact( new Object[]{"won"})); assertEquals("[won]", (String) ts1.invoke( new Object[]{"won"})); assertEquals("[won]", (String) ts1.invoke( "won" )); assertEquals("[[won]]", (String) ts1.invoke((Object) new Object[]{"won"})); // findStatic of Arrays.asList(...) produces a variable arity method handle: MethodHandle asList = publicLookup() .findStatic(Arrays.class, "asList", methodType(List.class, Object[].class)); assertEquals(methodType(List.class, Object[].class), asList.type()); assert(asList.isVarargsCollector()); assertEquals("[]", asList.invoke().toString()); assertEquals("[1]", asList.invoke(1).toString()); assertEquals("[two, too]", asList.invoke("two", "too").toString()); String[] argv = { "three", "thee", "tee" }; assertEquals("[three, thee, tee]", asList.invoke(argv).toString()); assertEquals("[three, thee, tee]", asList.invoke((Object[])argv).toString()); List ls = (List) asList.invoke((Object)argv); assertEquals(1, ls.size()); assertEquals("[three, thee, tee]", Arrays.toString((Object[])ls.get(0)));
解説: これらのルールは、可変引数メソッド用のJavaルールの動的型付けバリエーションとして設計されたものです。 どちらの場合も、可変引数メソッドまたはメソッド・ハンドルの呼出し元は、ゼロ以上の定位置引数を渡すことも、事前に収集された任意の長さの配列を渡すこともできます。 ユーザーは最後の引数の特殊な役割とその最後の引数での型一致の効果を認識すべきであり、その型一致によって、末尾の単一の引数が配列全体として解釈されるか、それとも収集対象となる配列の単一要素として解釈されるかが決まります。 後続引数の動的タイプは、このディシジョンには影響せず、コール・サイトのシンボリック・タイプ記述子とメソッド・ハンドルのタイプ記述子との比較のみであることに注意してください。
- パラメータ:
arrayType
- 通常はObject[]
で、複数の引数を集める配列引数の型- 戻り値:
- 末尾の任意の数の引数を1つの配列内に集めてから元のメソッド・ハンドルを呼び出すことができる新しいメソッド・ハンドル
- 例外:
NullPointerException
-arrayType
がnull参照の場合IllegalArgumentException
-arrayType
が配列型でないか、arrayType
をこのメソッド・ハンドルの末尾のパラメータの型に代入できない場合- 関連項目:
-
isVarargsCollector
public boolean isVarargsCollector()このメソッド・ハンドルが可変引数呼出しをサポートしているかどうかを判定します。 そのようなメソッド・ハンドルは次のソースから発生します。- asVarargsCollectorの呼出し
- 可変引数のJavaメソッドまたはコンストラクタに解決するルックアップ・メソッドの呼出し
- 可変引数のJavaメソッドまたはコンストラクタに解決する
CONSTANT_MethodHandle
のldc
命令
- 戻り値:
- このメソッド・ハンドルが厳密でないプレーンな
invoke
呼出しの複数の引数長を受け入れる場合はtrue - 関連項目:
-
asFixedArity
public MethodHandle asFixedArity()固定引数カウント・メソッド・ハンドル(その他の点では現在のメソッド・ハンドルと同等のもの)を作成します。現在のメソッド・ハンドルが可変引数でない場合、現在のメソッド・ハンドルが返されます。 これは、現在のメソッド・ハンドルを
asVarargsCollector
の有効な入力として指定できない場合でも言えることです。それ以外の場合、結果となる固定引数メソッド・ハンドルは現在のメソッド・ハンドルと基本的に同じ型と動作を備えていますが、
isVarargsCollector
がfalseである点だけは異なります。 固定アリティ・メソッド・ハンドルは、(またはできません)をasVarargsCollector
の前の引数にすることができます。リストを作成する可変引数メソッド・ハンドルの例を、次に示します。
MethodHandle asListVar = publicLookup() .findStatic(Arrays.class, "asList", methodType(List.class, Object[].class)) .asVarargsCollector(Object[].class); MethodHandle asListFix = asListVar.asFixedArity(); assertEquals("[1]", asListVar.invoke(1).toString()); Exception caught = null; try { asListFix.invoke((Object)1); } catch (Exception ex) { caught = ex; } assert(caught instanceof ClassCastException); assertEquals("[two, too]", asListVar.invoke("two", "too").toString()); try { asListFix.invoke("two", "too"); } catch (Exception ex) { caught = ex; } assert(caught instanceof WrongMethodTypeException); Object[] argv = { "three", "thee", "tee" }; assertEquals("[three, thee, tee]", asListVar.invoke(argv).toString()); assertEquals("[three, thee, tee]", asListFix.invoke(argv).toString()); assertEquals(1, ((List) asListVar.invoke((Object)argv)).size()); assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
- 戻り値:
- 固定された数の引数のみを受け入れる新しいメソッド・ハンドル
- 関連項目:
-
bindTo
public MethodHandle bindTo(Object x) 値x
をメソッド・ハンドルの最初の引数にバインドしますが、その呼出しは行いません。 新しいメソッド・ハンドルは、そのターゲットである現在のメソッド・ハンドルを適応させるため、指定された引数にターゲットをバインドします。 バインドされたハンドルの型はターゲットの型と基本的に同じになりますが、先頭の単一の参照パラメータが省略される点が異なります。バインドされたハンドルを呼び出すと、指定された値
x
が、ターゲットの新しい先頭の引数として挿入されます。 その他の引数も変更されずに渡されます。 ターゲットから最終的に返されたものが、そのまま変更されずにバインドされたハンドルから返されます。参照
x
は、ターゲットの最初のパラメータの型に変換できなければいけません。ノート: メソッド・ハンドルは不変であるため、ターゲット・メソッド・ハンドルは元の型と動作を保持します。
ノート: たとえ元のターゲット・メソッド・ハンドルがあったとしても、得られるアダプタは決して可変メソッド・ハンドルではありません。
- パラメータ:
x
- ターゲットの最初の引数にバインドする値- 戻り値:
- 入力引数リストの先頭に指定された値を追加してから元のメソッド・ハンドルを呼び出す新しいメソッド・ハンドル
- 例外:
IllegalArgumentException
- ターゲットの先頭のパラメータの型が参照型でない場合ClassCastException
-x
をターゲットの先頭のパラメータの型に変換できない場合- 関連項目:
-
describeConstable
public Optional<MethodHandleDesc> describeConstable()このインスタンスの名目記述子を返します(作成可能な場合)、作成できない場合は空のOptional
を返します。- 定義:
- インタフェース
Constable
内のdescribeConstable
- 戻り値:
- 最終的な名目記述子を含む
Optional
、または作成できない場合は空のOptional
。 - 導入されたバージョン:
- 12
-
toString
public String toString()メソッド・ハンドルの文字列表現を返しますが、これは、文字列"MethodHandle"
で始まり、メソッド・ハンドルの型の文字列表現で終わります。 つまり、このメソッドは次の値と等しい文字列を返します。"MethodHandle" + type().toString()
(ノート: このAPIの将来のリリースでは、さらなる情報が文字列表現に追加される可能性があります。 したがって、現在の構文をアプリケーション内で解析しないようにしてください。)
-