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

クラスMethodHandle

java.lang.Object
java.lang.invoke.MethodHandle
すべての実装されたインタフェース:
Constable

public abstract class MethodHandle extends Object implements 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命令が初めて実行されるとき、命令内の名前を記号的に解決し、メソッド呼び出しが静的に合法であることを確認することによってリンクされます。 これは、invokeExactinvokeへの呼び出しにも適用されます。 この場合、コンパイラによって発行されたシンボリック型記述子は、正しい構文がチェックされ、その中に含まれる名前が解決されます。 したがって、シンボリック型記述子が文法的に正しい形式であり、型が存在するかぎり、メソッド・ハンドルを呼び出す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_MethodrefCONSTANT_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であることを表明するメソッドであると想定されています。

例外

メソッドinvokeExactinvokeThrowableをスローするように宣言されていますが、これは、メソッド・ハンドルからスローできるものについて、静的な制限は一切ないことを示すためです。 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メソッド(または他のシグネチャ・ポリモーフィズム・メソッド)は、非仮想であるため、非仮想レシーバ・オブジェクト以外にメソッド・ハンドル自体の余分な引数を消費します。
これらの制限は、ある種のメソッド・ハンドルを作成できないことを意味します。スタックされた引数に関するJVM制限だけが原因です。 たとえば、static JVMメソッドが正確に255個の引数を受け入れる場合、それのメソッド・ハンドルは作成できません。 不可能なメソッド型でメソッド・ハンドルを作成しようとすると、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つの配列引数を受け取り、その要素を複数の定位置引数に分配するような、配列分配メソッド・ハンドルを作成します。
    asType(MethodType newType)
    現在のメソッド・ハンドルの型を新しい型に適応させるアダプタ・メソッド・ハンドルを生成します。
    asVarargsCollector(Class<?> arrayType)
    末尾の任意の数の定位置引数を受け取り、それらを集めて1つの配列引数に格納できるような、可変引数アダプタを作成します。
    xをメソッド・ハンドルの最初の引数にバインドしますが、その呼出しは行いません。
    このインスタンスの名目記述子を返します(作成可能な場合)、作成できない場合は空のOptionalを返します。
    final Object
    invoke(Object... args)
    メソッド・ハンドルを呼び出しますが、その際、呼出し元のどのような型記述子でも許可され、必要に応じて引数や戻り値の変換も実行されます。
    final Object
    メソッド・ハンドルを呼び出し、その際、呼出し元のどのような型記述子でも許可されますが、型は厳密に一致する必要があります。
    可変アリティ呼び出しを実行し、指定された配列の引数をメソッド・ハンドルに渡します。これは、Object型のみを記述する引数サイトの不正確なinvokeを経由するかのように、実際の引数の数は引数の配列の長さです。
    invokeWithArguments(List<?> arguments)
    可変アリティ呼び出しを実行し、指定されたリストの引数をメソッド・ハンドルに渡します。これは、Object型のみを記述する不正なinvokeを経由するかのように、実際の引数の数は引数リストの長さです。
    boolean
    このメソッド・ハンドルが可変引数呼出しをサポートしているかどうかを判定します。
    メソッド・ハンドルの文字列表現を返しますが、これは、文字列"MethodHandle"で始まり、メソッド・ハンドルの型の文字列表現で終わります。
    このメソッド・ハンドルの型を報告します。
    withVarargs(boolean makeVarargs)
    booleanフラグがtrueの場合は「可変アリティ」、そうでない場合は「固定アリティ」になるようにこのメソッド・ハンドルを修正します。

    クラス java.lang.Objectで宣言されたメソッド

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
  • メソッドの詳細

    • type

      public MethodType type()
      このメソッド・ハンドルの型を報告します。 invokeExactによるこのメソッド・ハンドルのすべての呼出しは、この型と厳密に一致する必要があります。
      戻り値:
      メソッド・ハンドルの型
    • invokeExact

      public final Object invokeExact(Object... args) throws Throwable
      メソッド・ハンドルを呼び出し、その際、呼出し元のどのような型記述子でも許可されますが、型は厳密に一致する必要があります。 invokeExactのコール・サイトのシンボリック型記述子は、このメソッド・ハンドルのと厳密に一致する必要があります。 引数や戻り値の変換は許可されません。

      Core Reflection API経由で監視した場合、このメソッドは、1つのオブジェクト配列を取って1つのオブジェクトを返す単一のネイティブ・メソッドのように見えます。 このネイティブ・メソッドが、java.lang.reflect.Method.invoke経由で直接的に呼び出されたり、JNI経由で呼び出されたり、あるいはLookup.unreflect経由で間接的に呼び出されたりすると、メソッドからUnsupportedOperationExceptionがスローされます。

      パラメータ:
      args - 可変引数で静的に表現された、シグネチャ・ポリモーフィズム・パラメータ・リスト
      戻り値:
      Objectで静的に表現された、シグネチャ・ポリモーフィズム結果
      例外:
      WrongMethodTypeException - ターゲットの型が呼出し元のシンボリック型記述子と同一でない場合
      Throwable - 配下のメソッドからスローされたものがすべて、そのまま変更されずにメソッド・ハンドル呼出し経由で伝播される
    • invoke

      public final Object invoke(Object... args) throws Throwable
      メソッド・ハンドルを呼び出しますが、その際、呼出し元のどのような型記述子でも許可され、必要に応じて引数や戻り値の変換も実行されます。

      コール・サイトのシンボリック型記述子がこのメソッド・ハンドルのと厳密に一致する場合、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

      public Object invokeWithArguments(Object... arguments) throws Throwable
      可変アリティ呼び出しを実行し、指定された配列の引数をメソッド・ハンドルに渡します。これは、Object型のみを記述する引数サイトの不正確なinvokeを経由するかのように、実際の引数の数は引数の配列の長さです。

      具体的には次のステップのように実行が進みますが、JVMがメソッド呼出しの効果を予測できる場合はメソッドが呼び出される保証はありません。

      • 引数配列の長さ(N)を決定します。 null参照の場合、N=0になります。
      • 配列のN要素を論理引数リストとして収集し、各引数は静的にObjectと入力します。
      • Mとして、このメソッド・ハンドルのタイプのパラメータ・カウントを判別します。
      • N引数またはM引数の一般的なタイプTNNより小さい場合はTN=MethodType.genericMethodType(Math.min(N, M))として決定します。
      • NMより大きい場合は、次のチェックとアクションを実行して、論理引数リストを短縮します:
        • このメソッド・ハンドルが、配列型A[]「後続パラメータ」で可変アリティを持っていることを確認してください。 そうでない場合は、WrongMethodTypeExceptionで失敗します。
        • 後続の要素(それらのN-M+1があります)を論理引数リストからA[]型の単一の配列に集め、asType変換を使用して、後続の各引数をA型に変換します。
        • これらの変換のいずれかが不可能であると判明した場合、後続の要素がAにキャストできない場合はClassCastExceptionで、後続要素がnullAが参照型でない場合は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はシグネチャ・ポリモーフィズム・メソッドのinvokeExactinvokeとは異なり、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

      public Object invokeWithArguments(List<?> arguments) throws Throwable
      可変アリティ呼び出しを実行し、指定されたリストの引数をメソッド・ハンドルに渡します。これは、Object型のみを記述する不正なinvokeを経由するかのように、実際の引数の数は引数リストの長さです。

      このメソッドは次のコードとも同等です。

      
         invokeWithArguments(arguments.toArray())
       

      このメソッド・ハンドルが可変アリティを持つ場合、ジャンボ・サイズのリストは許容されます。 詳細については、invokeWithArguments(Object[])を参照してください。

      パラメータ:
      arguments - ターゲットに渡す引数
      戻り値:
      ターゲットから返された結果
      例外:
      NullPointerException - argumentsがnull参照の場合
      ClassCastException - 引数が参照キャストで変換できない場合
      WrongMethodTypeException - 指定された数のObject引数を取るようにターゲットの型を調整できない場合
      Throwable - ターゲット・メソッド呼出しでスローされたすべてのオブジェクト
    • asType

      public MethodHandle asType(MethodType newType)
      現在のメソッド・ハンドルの型を新しい型に適応させるアダプタ・メソッド・ハンドルを生成します。 結果のメソッド・ハンドルが目的の新しい型と等しい型を報告することが、保証されます。

      元の型と新しい型が等しい場合はthisを返します。

      新しいメソッド・ハンドルを呼び出すと、次のステップが実行されます。

      • 入力引数リストを変換し、元のメソッド・ハンドルの引数リストに一致させます。
      • 変換後の引数リストで元のメソッド・ハンドルを呼び出します。
      • 元のメソッド・ハンドルから返された結果を、新しいメソッド・ハンドルの戻り値の型に変換します。

      このメソッドのために、invokeExactと厳密でないプレーンなinvokeとの間で動作が大きく異なります。 2つのメソッドは、呼び出し元の型記述子が呼び出し先と正確に一致する場合と同じステップを実行しますが、型が異なる場合、呼び出し元と呼び出し先の型を一致させるために、asType (または内部相当物)も呼び出します。

      現在のメソッドが可変引数メソッド・ハンドルの場合、別の場所で説明しているように、引数リストの変換時にいくつかの引数の1つの配列内への変換と収集が行われる可能性があります。 その他のすべての場合には、あらゆる変換はペアで適用され、それぞれの引数または戻り値は厳密に1つの引数または戻り値(または戻り値なし)に変換されます。 適用された変換は、古いメソッド・ハンドル・タイプと新しいメソッド・ハンドル・タイプの対応するコンポーネント・タイプを調べることによって定義されます。

      T0T1を、対応する新しいパラメータの型と古いパラメータの型、あるいは古い戻り値の型と新しい戻り値の型とします。 具体的には、ある有効なインデックスiについて、T0=newType.parameterType(i)T1=this.type().parameterType(i)とします。 また、戻り値については、T0=this.type().returnType()T1=newType.returnType()とします。 型が同じである場合、新しいメソッド・ハンドルは、対応する引数または戻り値(存在する場合)に対して何の変更も加えません。 それ以外の場合、可能であれば次のいずれかの変換が適用されます。

      • T0T1が参照の場合、T1へのキャストが適用されます。 (型同士が何らかの形で関連している必要はありません。 これは、nullの動的な値はどのような参照型にも変換できるからです。)
      • T0T1がプリミティブの場合、Javaメソッド呼出し変換(JLS 5.3)が適用されます(存在する場合)。 (具体的には、T0T1に変換するには、プリミティブ・ワイドニング変換を行う必要があります。)
      • 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がプリミティブの場合、値ゼロが導入されます。
      (ノート: T0T1はどちらも静的な型とみなせますが、それは、どちらも何らかの実際の引数または戻り値の動的な型に具体的に対応しているわけではないからです。)

      必要なペア単位の変換のいずれかを行えない場合は、メソッド・ハンドルの変換を行えません。

      参照の引数や戻り値に適用される変換では、実行時に、失敗する可能性のある追加の実行時チェックが必要になる可能性があります。 アンボクシング操作は、元の参照が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_MethodHandleldc命令
      戻り値:
      このメソッド・ハンドルが厳密でないプレーンな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の将来のリリースでは、さらなる情報が文字列表現に追加される可能性があります。 したがって、現在の構文をアプリケーション内で解析しないようにしてください。)

      オーバーライド:
      toString、クラスObject
      戻り値:
      メソッド・ハンドルの文字列表現