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

クラスMethodHandles



  • public class MethodHandles
    extends Object
    このクラスは、メソッド・ハンドルに対する処理を行うかメソッド・ハンドルを返すstaticメソッドだけで構成されます。 それらはいくつかのカテゴリに分類されます。
    • メソッドやフィールドのメソッド・ハンドルの作成に役立つルックアップ・メソッド。
    • 既存のメソッド・ハンドルを組み合わせたり変換したりして新しいハンドルを作成するコンビネータ・メソッド。
    • その他の一般的なJVM操作や制御フロー・パターンをエミュレートするメソッド・ハンドルを作成する、その他のファクトリ・メソッド。
    導入されたバージョン:
    1.7
    • メソッドの詳細

      • lookup

        public static MethodHandles.Lookup lookup​()
        呼出し元のすべてのサポートされるバイトコード動作をエミュレートするためのフル機能を持つルックアップ・オブジェクトを返します。 これらの機能には呼出し元へのprivateアクセスが含まれます。 ルックアップ・オブジェクトのファクトリ・メソッドは、呼出し元がバイトコードを介してアクセスするメンバー(protectedおよびprivateのフィールドおよびメソッドを含む)の直接メソッド・ハンドルを作成できます。 このルックアップ・オブジェクトは、信頼できるエージェントに委譲可能な1つの機能です。 信頼できないコードからアクセス可能な場所に格納しないでください。

        このメソッドは呼出し元依存です。つまり、呼出し元ごとに異なる値を返す可能性があります。

        呼出し元クラスCが指定された場合、この呼出しによって返されるルックアップ・オブジェクトは、同じ呼出し元クラスCで実行するinvokedynamic命令のブートストラップ・メソッドに対してJVMによって提供されるルックアップ・オブジェクトと同等の機能を持ちます。

        戻り値:
        このメソッドの呼出し元用のルックアップ・オブジェクト、privateアクセス
      • publicLookup

        public static MethodHandles.Lookup publicLookup​()
        最小の信頼レベルを持つルックアップ・オブジェクトを返します。 ルックアップには、PUBLICおよびUNCONDITIONALモードがあります。 これは、無条件にエクスポートされたパッケージ内のパブリック・クラスのパブリック・メンバーへのメソッド・ハンドルの作成にのみ使用できます。

        単に慣例により、このルックアップ・オブジェクトのルックアップ・クラスは、Objectになります。

        APIの注:
        Objectの使用は従来のものであり、ルックアップ・モードが限られているため、Object、そのパッケージまたはそのモジュールの内部に特別なアクセスは提供されていません。 したがって、このルックアップ・オブジェクトのルックアップ・コンテキストはブートストラップ・クラス・ローダーになります。つまり、ユーザー・クラスを見つけることができません。

        討論: ルックアップ・クラスは、publicLookup().in(C.class)という形式の式を使用して、他のクラスCに変更できます。クラス・ローダーを変更することによってルックアップ・コンテキストを変更する可能性があります。 publicルックアップ・オブジェクトは常にセキュリティ・マネージャ・チェックが適用されます。 また、呼出し元依存メソッドにアクセスすることもできません。

        戻り値:
        最小限の信頼されるルックアップ・オブジェクト
      • privateLookupIn

        public static MethodHandles.Lookup privateLookupIn​(Class<?> targetClass,
                                                           MethodHandles.Lookup lookup)
                                                    throws IllegalAccessException
        「プライベート・アクセス」を含む、サポートされているすべてのバイトコード動作をエミュレートする完全な機能を備えたlookup objectを返します。 このメソッドは、Lookupオブジェクトとして指定された呼び出し元が、対象クラスの「深い反射」を実行できることを確認します。 m1lookup classを含むモジュールで、m2がターゲット・クラスを含むモジュールである場合、このチェックは次のことを保証
        • m1 reads m2
        • m2 opens少なくともm1へのターゲット・クラスを含むパッケージ。
        • ルックアップにはMODULE参照モードがあります。

        セキュリティ・マネージャがある場合、checkPermissionメソッドがReflectPermission("suppressAccessChecks")をチェックするために呼び出されます。

        APIの注:
        MODULEルックアップ・モードは、呼び出し側モジュール(呼び出し元によって最初に作成された参照オブジェクトから派生したもの)のコードによってルックアップ・オブジェクトが作成されたことを認証します。 MODULEルックアップ・モードのルックアップ・オブジェクトは、PRIVATEPACKAGEを呼び出し元にアクセスさせずに、信頼できる相手と共有することができます。
        パラメータ:
        targetClass - ターゲット・クラス
        lookup - 呼び出し元参照オブジェクト
        戻り値:
        ターゲット・クラスのルックアップ・オブジェクト。プライベート・アクセス
        例外:
        IllegalArgumentException - targetClassがプリミティブ型または配列クラスの場合
        NullPointerException - targetClassまたはcallernullの場合
        IllegalAccessException - 上記のアクセス・チェックが失敗した場合
        SecurityException - セキュリティ・マネージャによって拒否された場合
        導入されたバージョン:
        9
        関連項目:
        MethodHandles.Lookup.dropLookupMode(int)
      • reflectAs

        public static <T extends Member> T reflectAs​(Class<T> expected,
                                                     MethodHandle target)
        直接メソッド・ハンドルの未チェックの解読を実行します。 結果は、ユーザーがターゲット・メソッド・ハンドルを解決するのに十分な機能を持つルックアップ・オブジェクトを取得し、ターゲットでLookup.revealDirectを呼び出してシンボリック参照を取得し、MethodHandleInfo.reflectAsを呼び出してシンボリック参照をメンバーに解決したかのようになります。

        セキュリティ・マネージャが存在する場合は、そのcheckPermissionメソッドがReflectPermission("suppressAccessChecks")アクセス権で呼び出されます。

        型パラメータ:
        T - 結果に期待する型(Memberまたはサブタイプ)
        パラメータ:
        target - シンボリック参照コンポーネントに解決する直接メソッド・ハンドル
        expected - 期待する結果型Tを表すクラス・オブジェクト
        戻り値:
        メソッド、コンストラクタまたはフィールド・オブジクトへの参照
        例外:
        SecurityException - 呼出し元にsetAccessibleを呼び出す権限が与えられていない場合
        NullPointerException - どちらかの引数がnullの場合
        IllegalArgumentException - ターゲットが直接メソッド・ハンドルでない場合
        ClassCastException - メンバーが期待される型でない場合
        導入されたバージョン:
        1.8
      • arrayConstructor

        public static MethodHandle arrayConstructor​(Class<?> arrayClass)
                                             throws IllegalArgumentException
        目的の型の配列を構築するメソッド・ハンドルを生成します。 メソッド・ハンドルの戻り値の型は配列型になります。 唯一の引数の型は、配列のサイズを指定するintです。
        パラメータ:
        arrayClass - 配列の型
        戻り値:
        指定された型の配列を作成できるメソッド・ハンドル
        例外:
        NullPointerException - 引数がnullの場合
        IllegalArgumentException - arrayClassが配列型でない場合
        導入されたバージョン:
        9
        関連項目:
        Array.newInstance(Class, int)
      • arrayLength

        public static MethodHandle arrayLength​(Class<?> arrayClass)
                                        throws IllegalArgumentException
        配列の長さを返すメソッド・ハンドルを生成します。 メソッド・ハンドルの型は戻り型としてintを持ち、唯一の引数は配列型です。
        パラメータ:
        arrayClass - 配列の型
        戻り値:
        指定された配列型の配列の長さを取得できるメソッド・ハンドル
        例外:
        NullPointerException - 引数がnullの場合
        IllegalArgumentException - arrayClassが配列型でない場合
        導入されたバージョン:
        9
      • arrayElementGetter

        public static MethodHandle arrayElementGetter​(Class<?> arrayClass)
                                               throws IllegalArgumentException
        配列の各要素に対する読取りアクセスを提供するメソッド・ハンドルを生成します。 メソッド・ハンドルの型に含まれる戻り値の型は、配列の要素の型になります。 その最初の引数は配列の型、2番目の引数はintになります。
        パラメータ:
        arrayClass - 配列の型
        戻り値:
        指定された配列型から値をロードできるメソッド・ハンドル
        例外:
        NullPointerException - 引数がnullの場合
        IllegalArgumentException - arrayClassが配列型でない場合
      • arrayElementSetter

        public static MethodHandle arrayElementSetter​(Class<?> arrayClass)
                                               throws IllegalArgumentException
        配列の各要素に対する書込みアクセスを提供するメソッド・ハンドルを生成します。 このメソッド・ハンドルの型に含まれる戻り値の型は、voidになります。 その最後の引数は、配列の要素の型になります。 最初と2番目の引数は、配列の型とintになります。
        パラメータ:
        arrayClass - 配列のクラス
        戻り値:
        配列型に値を格納できるメソッド・ハンドル
        例外:
        NullPointerException - 引数がnullの場合
        IllegalArgumentException - arrayClassが配列型でない場合
      • arrayElementVarHandle

        public static VarHandle arrayElementVarHandle​(Class<?> arrayClass)
                                               throws IllegalArgumentException
        arrayClassの配列の要素にアクセスするVarHandleを生成します。 VarHandle変数型はarrayClassのコンポーネント型であり、座標型のリストは(arrayClass, int)であり、int座標型は配列へのインデックスである引数に対応します。

        返されたVarHandleの特定のアクセス・モードは、次の条件ではサポートされません:

        • コンポーネント型がbyteshortcharintlongfloat、またはdouble以外の場合、数値アトミック更新アクセス・モードはサポートされていません。
        • フィールド型がbooleanbyteshortcharintまたはlong以外の場合、ビット単位アトミック更新アクセス・モードはサポートされていません。

        コンポーネント型がfloatまたはdoubleの場合、数値およびアトミック更新アクセス・モードはビット単位の表現(それぞれFloat.floatToRawIntBits(float)Double.doubleToRawLongBits(double)を参照してください)を使用して値を比較します。

        APIの注:
        数値およびアトミック更新アクセス・モードで実行されるfloat値またはdouble値のビット単位の比較は、基本的な==演算子およびFloat.equals(java.lang.Object)およびDouble.equals(java.lang.Object)メソッドとは異なります(NaN値の比較または-0.0+0.0の比較を参照)。 操作が予期せず失敗する可能性があるため、このような値との比較および設定、比較および交換操作を実行するときは注意が必要です。 JavaではNaNと見なされるNaN値は数多くありますが、Javaが提供するIEEE 754浮動小数点演算では区別できません。 予想される値または目撃者の値がNaN値であり、(おそらくプラットフォーム固有の方法で)が別のNaN値に変換され、したがって異なるビット単位の表現(詳細はFloat.intBitsToFloat(int)またはDouble.longBitsToDouble(long)を参照してください)を持つ場合、操作の失敗が発生する可能性があります。 -0.0+0.0の値は、ビット単位の表現が異なりますが、プリミティブ==演算子を使用する場合、等しいとみなされます。 たとえば、数値アルゴリズムが期待値を-0.0と計算し、前もって+0.0と呼ばれる証人値を計算した場合、演算エラーが発生する可能性があります。
        パラメータ:
        arrayClass - T[]型の配列のクラス
        戻り値:
        配列の要素にアクセスするVarHandle
        例外:
        NullPointerException - arrayClassがnullの場合
        IllegalArgumentException - arrayClassが配列型でない場合
        導入されたバージョン:
        9
      • byteArrayViewVarHandle

        public static VarHandle byteArrayViewVarHandle​(Class<?> viewArrayClass,
                                                       ByteOrder byteOrder)
                                                throws IllegalArgumentException
        byte[]配列の要素にアクセスするVarHandleを生成し、int[]long[]などの異なるプリミティブ配列型であるかのように見せます。 VarHandle変数型はviewArrayClassのコンポーネント型であり、座標型のリストは(byte[], int)であり、int座標型はbyte[]配列へのインデックスである引数に対応します。 返されたVarHandleは、指定されたエンディアンに従って、viewArrayClassのコンポーネント型の値との間でバイトを構成するbyte[]配列のインデックスのバイトにアクセスします。

        サポートされるコンポーネント型(変数の型)は、shortcharintlongfloatおよびdoubleである。

        インデックスが0より小さい場合、またはbyte[]配列の長さからTのサイズ(バイト単位)を引いた値より大きい場合、特定のインデックスでバイトにアクセスすると、IndexOutOfBoundsExceptionになります。

        インデックスのバイトのアクセスは、Tに対して、配列とインデックスに関連付けられている下位のメモリー・アドレスAに対して、整列されていたり、位置がずれている可能性があります。 アクセスが整列していない場合、getおよびsetアクセス・モード以外のアクセスは、IllegalStateExceptionになります。 そのような場合、アトミック・アクセスは、AのGCDとTのサイズ(バイト単位)を分割する2の最大の累乗に関してのみ保証されます。 アクセスが整列されている場合、次のアクセス・モードがサポートされ、アトミック・アクセスをサポートすることが保証されています:

        • 32ビット・プラットフォーム上のlongおよびdoubleのアクセス・モードgetおよびsetを除き、すべてのTの読み取り/書き込みアクセス・モード。
        • intlongfloatまたはdoubleのアトミック更新アクセス・モード。 (JDKの今後の主要なプラットフォーム・リリースは、現在サポートされていない特定のアクセス・モードに対して追加の型をサポートする可能性があります。)
        • intおよびlongの数値アトミック更新アクセス・モード。 (JDKの将来の主要なプラットフォーム・リリースは、現在サポートされていない特定のアクセス・モードに対して、追加の数値型をサポートする可能性があります。)
        • intlongのビット単位アトミック更新アクセス・モード。 (JDKの将来の主要なプラットフォーム・リリースは、現在サポートされていない特定のアクセス・モードに対して、追加の数値型をサポートする可能性があります。)

        特定のアレイ上で動作することなく、byte[]配列に対して誤整列アクセス、したがってアトミック性の保証が決定される可能性があります。 indexTが与えられ、それに対応するボックス化された型T_BOXが与えられると、誤整列は以下のように決定される:

        
         int sizeOfT = T_BOX.BYTES;  // size in bytes of T
         int misalignedAtZeroIndex = ByteBuffer.wrap(new byte[0]).
             alignmentOffset(0, sizeOfT);
         int misalignedAtIndex = (misalignedAtZeroIndex + index) % sizeOfT;
         boolean isMisaligned = misalignedAtIndex != 0;
         

        変数の型がfloatまたはdoubleの場合、アトミック更新アクセス・モードはビット単位の表現(それぞれFloat.floatToRawIntBits(float)Double.doubleToRawLongBits(double)を参照してください)を使用して値を比較します。

        パラメータ:
        viewArrayClass - T型のコンポーネント型を持つビュー配列クラス
        byteOrder - 基になるbyte配列に格納されているビュー配列要素のエンディアン
        戻り値:
        byte[]配列の要素にアクセスするVarHandleは、ビュー配列クラスのコンポーネント型に対応する要素として見なされます
        例外:
        NullPointerException - viewArrayClassまたはbyteOrderがnullの場合
        IllegalArgumentException - viewArrayClassが配列型でない場合
        UnsupportedOperationException - viewArrayClassのコンポーネント型が変数型としてサポートされていない場合
        導入されたバージョン:
        9
      • byteBufferViewVarHandle

        public static VarHandle byteBufferViewVarHandle​(Class<?> viewArrayClass,
                                                        ByteOrder byteOrder)
                                                 throws IllegalArgumentException
        int[]またはlong[]など、byteの要素にアクセスするVarHandleを生成します。これは、ByteBufferの要素と異なるプリミティブ・コンポーネント型の要素の配列であるかのように見えます。 VarHandle変数型はviewArrayClassのコンポーネント型であり、座標型のリストは(ByteBuffer, int)であり、int座標型はbyte[]配列へのインデックスである引数に対応します。 返されたVarHandleは、指定されたエンディアンに従って、viewArrayClassのコンポーネント型の値との間でバイトを構成するByteBufferのインデックスでバイトにアクセスします。

        サポートされるコンポーネント型(変数の型)は、shortcharintlongfloatおよびdoubleである。

        ByteBufferが読み取り専用の場合、アクセスは読み取りアクセス・モード以外の場合はReadOnlyBufferExceptionになります。

        インデックスが0より小さい場合、またはByteBuffer制限からTのサイズ(バイト単位)を引いた値より大きい場合、特定のインデックスでバイトにアクセスすると、IndexOutOfBoundsExceptionになります。

        インデックスのバイトのアクセスは、Tの場合、ByteBufferおよびindexに関連付けられている下位のメモリー・アドレスAに対して、整列されていたり、位置がずれている可能性があります。 アクセスが整列していない場合、getおよびsetアクセス・モード以外のアクセスは、IllegalStateExceptionになります。 そのような場合、アトミック・アクセスは、AのGCDとTのサイズ(バイト単位)を分割する2の最大の累乗に関してのみ保証されます。 アクセスが整列されている場合、次のアクセス・モードがサポートされ、アトミック・アクセスをサポートすることが保証されています:

        • 32ビット・プラットフォーム上のlongおよびdoubleのアクセス・モードgetおよびsetを除き、すべてのTの読み取り/書き込みアクセス・モード。
        • intlongfloatまたはdoubleのアトミック更新アクセス・モード。 (JDKの今後の主要なプラットフォーム・リリースは、現在サポートされていない特定のアクセス・モードに対して追加の型をサポートする可能性があります。)
        • intおよびlongの数値アトミック更新アクセス・モード。 (JDKの将来の主要なプラットフォーム・リリースは、現在サポートされていない特定のアクセス・モードに対して、追加の数値型をサポートする可能性があります。)
        • intlongのビット単位アトミック更新アクセス・モード。 (JDKの将来の主要なプラットフォーム・リリースは、現在サポートされていない特定のアクセス・モードに対して、追加の数値型をサポートする可能性があります。)

        ByteBufferbb (直接的またはその他の方法で)、indexTに対して、整列されていないアクセス、したがってアトミック性の保証が決定され、対応するボックス化された型T_BOXが次のようになります:

        
         int sizeOfT = T_BOX.BYTES;  // size in bytes of T
         ByteBuffer bb = ...
         int misalignedAtIndex = bb.alignmentOffset(index, sizeOfT);
         boolean isMisaligned = misalignedAtIndex != 0;
         

        変数の型がfloatまたはdoubleの場合、アトミック更新アクセス・モードはビット単位の表現(それぞれFloat.floatToRawIntBits(float)Double.doubleToRawLongBits(double)を参照してください)を使用して値を比較します。

        パラメータ:
        viewArrayClass - T型のコンポーネント型を持つビュー配列クラス
        byteOrder - ByteBuffer (これはByteBufferのエンディアンを上書きすることに注意してください)に格納されているビュー配列要素のエンディアン
        戻り値:
        ByteBufferの要素にアクセスするVarHandleは、ビュー配列クラスのコンポーネントの型に対応する要素のように見なされます
        例外:
        NullPointerException - viewArrayClassまたはbyteOrderがnullの場合
        IllegalArgumentException - viewArrayClassが配列型でない場合
        UnsupportedOperationException - viewArrayClassのコンポーネント型が変数型としてサポートされていない場合
        導入されたバージョン:
        9
      • spreadInvoker

        public static MethodHandle spreadInvoker​(MethodType type,
                                                 int leadingArgCount)
        指定されたtypeの任意のメソッド・ハンドルを呼び出すメソッド・ハンドルを生成しますが、その際、指定された数の末尾の引数が単一の末尾のObject[]配列で置き換えられます。 結果となるインボーカは、次の引数を持つメソッド・ハンドルです。
        • 単一のMethodHandleターゲット
        • 0個以上の先頭の値(個数はleadingArgCount)
        • 末尾の引数を含むObject[]配列

        インボーカがそのターゲットを呼び出す方法は、示されたtypeを使用したinvokeの呼出しに似ています。 つまりその動作は、ターゲットの型が指定されたtypeと完全に等しい場合はinvokeExactのようになり、それ以外の場合は、asTypeを使ってターゲットを必要なtypeに変換するような動作になります。

        返されるインボーカの型は、指定されたtypeではなく、最初のleadingArgCount個を除くすべてのパラメータが単一のObject[]型配列で置き換えられたものとなり、これが最後のパラメータになります。

        インボーカはそのターゲットを呼び出す前に、最後の配列を分配し、参照キャストを必要に応じて適用するほか、プリミティブ引数のアンボクシングやワイドニングを行います。 インボーカが呼び出されるときに、渡される配列引数が正しい数の要素を持たない場合、インボーカはターゲットを呼び出すかわりにIllegalArgumentExceptionをスローします。

        このメソッドは次のコードと同等です(ただし、効率はおそらくこのメソッドのほうが高い)。

        
        MethodHandle invoker = MethodHandles.invoker(type);
        int spreadArgCount = type.parameterCount() - leadingArgCount;
        invoker = invoker.asSpreader(Object[].class, spreadArgCount);
        return invoker;
         
        このメソッドでは、リフレクションやセキュリティに関する例外はスローされません。
        パラメータ:
        type - 目的となるターゲットの型
        leadingArgCount - ターゲットに無変更で渡される固定引数の数
        戻り値:
        指定された型の任意のメソッド・ハンドルの呼出しに適したメソッド・ハンドル
        例外:
        NullPointerException - typeがnullである場合
        IllegalArgumentException - leadingArgCountが0からtype.parameterCount()(含む)までの範囲でない場合、または結果のメソッド・ハンドルの型のパラメータ数が多すぎる場合
      • exactInvoker

        public static MethodHandle exactInvoker​(MethodType type)
        特殊なインボーカ・メソッド・ハンドルを生成します(これを使用すれば、指定された型の任意のメソッド・ハンドルを、invokeExactを使用する場合と同様に呼び出すことができる)。 結果となるインボーカの型は、MethodHandle型の追加の先頭の引数を1つ受け取る点を除けば、目的の型とまったく等しくなります。

        このメソッドは次のコードと同等です(ただし、効率はおそらくこのメソッドのほうが高い)。 publicLookup().findVirtual(MethodHandle.class, "invokeExact", type)

        解説: インボーカ・メソッド・ハンドルは、未知の型のメソッド・ハンドル変数を操作する場合に役立つ可能性があります。 たとえば、メソッド・ハンドル変数MへのinvokeExact呼出しをエミュレートするには、その型Tを抽出し、T用のインボーカ・メソッドXを検索し、インボーカ・メソッドをX.invoke(T, A...)のように呼び出します。 (型Tが未知であるため、X.invokeExactの呼出しは機能しない。) 分配や収集などの引数変換が必要な場合は、それらをインボーカXに一度だけ適用しておけば、Mのさまざまなメソッド・ハンドル値(ただしXの型と互換性があるものにかぎる)で再利用できます。

        (注: Core Reflection API経由でインボーカ・メソッドを使用することはできません。 宣言されたinvokeExactまたはinvokeメソッドでjava.lang.reflect.Method.invokeを呼び出そうとすると、UnsupportedOperationExceptionが発行されます。)

        このメソッドでは、リフレクションやセキュリティに関する例外はスローされません。

        パラメータ:
        type - 目的となるターゲットの型
        戻り値:
        指定された型の任意のメソッド・ハンドルの呼出しに適したメソッド・ハンドル
        例外:
        IllegalArgumentException - 結果のメソッド・ハンドルの型のパラメータ数が多すぎる場合
      • invoker

        public static MethodHandle invoker​(MethodType type)
        特殊なインボーカ・メソッド・ハンドルを生成します(これを使用すれば、指定された型と互換性のある任意のメソッド・ハンドルを、invokeを使用する場合と同様に呼び出すことができる)。 結果となるインボーカの型は、MethodHandle型の追加の先頭の引数を1つ受け取る点を除けば、目的の型とまったく等しくなります。

        ターゲットが期待される型と異なっている場合、インボーカはそのターゲットを呼び出す前に、asTypeの場合と同じように、参照キャストを必要に応じて適用するほか、プリミティブ値のボクシング、アンボクシング、またはワイドニングを行います。 同様に、戻り値も必要に応じて変換されます。 ターゲットが可変引数メソッド・ハンドルの場合は、やはりasTypeの場合と同じように、必要な引数変換が行われます。

        このメソッドは次のコードと同等です(ただし、効率はおそらくこのメソッドのほうが高い)。 publicLookup().findVirtual(MethodHandle.class, "invoke", type)

        ディスカッション: 一般的なメソッド型は、Object引数および戻り値のみを言及するものです。 そのような型のインボーカは、引数の数が汎用型と同じメソッド・ハンドルであれば任意のものを呼び出すことができます。

        (注: Core Reflection API経由でインボーカ・メソッドを使用することはできません。 宣言されたinvokeExactまたはinvokeメソッドでjava.lang.reflect.Method.invokeを呼び出そうとすると、UnsupportedOperationExceptionが発行されます。)

        このメソッドでは、リフレクションやセキュリティに関する例外はスローされません。

        パラメータ:
        type - 目的となるターゲットの型
        戻り値:
        指定された型に変換可能な任意のメソッド・ハンドルの呼出しに適したメソッド・ハンドル
        例外:
        IllegalArgumentException - 結果のメソッド・ハンドルの型のパラメータ数が多すぎる場合
      • varHandleExactInvoker

        public static MethodHandle varHandleExactInvoker​(VarHandle.AccessMode accessMode,
                                                         MethodType type)
        関連付けられたアクセス・モードの型が指定された型と互換性のあるVarHandle上で、シグネチャ・ポリモーフィック・アクセス・モード・メソッドを呼び出すために使用できる特別な「呼び出しメソッドhandle」を生成します。 結果の呼び出し側の型は、型VarHandleの追加の引き数を受け入れることを除いて、指定された型とまったく同じ型を持ちます。
        パラメータ:
        accessMode - VarHandleアクセス・モード
        type - 目的となるターゲットの型
        戻り値:
        アクセス・モードの型が指定された型のVarHandleのアクセス・モード・メソッドを呼び出すのに適したメソッド・ハンドル。
        導入されたバージョン:
        9
      • varHandleInvoker

        public static MethodHandle varHandleInvoker​(VarHandle.AccessMode accessMode,
                                                    MethodType type)
        関連付けられたアクセス・モードの型が指定された型と互換性のあるVarHandle上で、シグネチャ・ポリモーフィック・アクセス・モード・メソッドを呼び出すために使用できる特別な「呼び出しメソッドhandle」を生成します。 結果の呼び出し側の型は、型VarHandleの追加の引き数を受け入れることを除いて、指定された型とまったく同じ型を持ちます。

        ターゲットを呼び出す前に、アクセス・モードの型が目的の型と異なる場合、呼び出し側はasTypeの場合と同様に、必要に応じて参照キャストを適用し、ボックス、アンボックス、またはプリミティブ値を拡大します。 同様に、戻り値も必要に応じて変換されます。

        このメソッドは、次のコードと同じです。(より効率的かもしれませんが): publicLookup().findVirtual(VarHandle.class, accessMode.name(), type)

        パラメータ:
        accessMode - VarHandleアクセス・モード
        type - 目的となるターゲットの型
        戻り値:
        アクセス・モードの型が指定された型に変換可能なVarHandleのアクセス・モード・メソッドを呼び出すのに適したメソッド・ハンドル。
        導入されたバージョン:
        9
      • explicitCastArguments

        public static MethodHandle explicitCastArguments​(MethodHandle target,
                                                         MethodType newType)
        指定されたメソッド・ハンドルの型を新しい型に適応させるために、引数と戻り値の型の変換をペア単位で行うメソッド・ハンドルを生成します。 元の型と新しい型は同じ数の引数を備えている必要があります。 結果のメソッド・ハンドルが目的の新しい型と等しい型を報告することが、保証されます。

        元の型と新しい型が等しい場合はターゲットを返します。

        MethodHandle.asTypeの場合と同じ変換が許可されるほか、それらの変換が失敗した場合には、いくつかの追加の変換も適用されます。 asTypeで行われる変換の前に、あるいはその代わりに、次のいずれかの変換が可能であれば適用されます(T0T1は型)。

        • T0T1が参照であり、T1がインタフェース型である場合は、型T0の値がキャストなしでT1として渡されます。 (インタフェースのこの扱いは、バイトコード・ベリファイアの使用法に従ったものです。)
        • T0がbooleanでT1が別のプリミティブの場合、booleanがバイト値に変換されます(trueは1、falseは0)。 (この扱いは、バイトコード・ベリファイアの使用法に従ったものです。)
        • T1がbooleanでT0が別のプリミティブの場合、Javaキャスト変換(JLS 5.5)によってT0がbyteに変換され、結果の下位ビットのテストが、(x & 1)!= 0と同様に行われます。
        • T0T1がboolean以外のプリミティブの場合、Javaキャスト変換(JLS 5.5)が適用されます。 (具体的には、ワイドニングやナローイングによってT0T1に変換される。)
        • T0が参照でT1がプリミティブの場合は、実行時にアンボクシング変換が適用されますが、そのあと、プリミティブ値に対してJavaキャスト変換(JLS 5.5)が行われる可能性があり、さらに下位ビットのテストによるbyteからbooleanへの変換が行われる可能性もあります。
        • T0が参照でT1がプリミティブの場合、実行時に参照がnullであれば、値ゼロが導入されます。
        パラメータ:
        target - 引数の型を調整したあとに呼び出すメソッド・ハンドル
        newType - 新しいメソッド・ハンドルの期待される型
        戻り値:
        必要な引数変換をすべて実行したあとでターゲットに委譲し、必要なあらゆる戻り値変換の手配も行うメソッド・ハンドル
        例外:
        NullPointerException - どちらかの引数がnullの場合
        WrongMethodTypeException - 変換できない場合
        関連項目:
        MethodHandle.asType(java.lang.invoke.MethodType)
      • permuteArguments

        public static MethodHandle permuteArguments​(MethodHandle target,
                                                    MethodType newType,
                                                    int... reorder)
        引数の順序を変更することによって、指定されたメソッド・ハンドルの呼出し順序を新しい型に適応させるメソッド・ハンドルを生成します。 結果のメソッド・ハンドルが目的の新しい型と等しい型を報告することが、保証されます。

        指定された配列によって並べ替えが制御されます。 入力パラメータの数(値newType.parameterCount())を#I、出力パラメータの数(値target.type().parameterCount())を#Oとします。 このとき、並べ替え配列の長さは#O、各要素は#Iより小さい負でない数でなければいけません。 #Oより小さいすべてのNについて、N番目の出力引数はI番目の入力引数から取られます(Ireorder[N])。

        引数や戻り値の変換は一切適用されません。 各入力引数の型(newTypeによって決定される)は、ターゲット・メソッド・ハンドルの対応する1つまたは複数の出力パラメータの型と同一でなければいけません。 newTypeの戻り値の型は、元のターゲットの戻り値の型と同一でなければいけません。

        並べ替え配列では、実際の入れ替えを指定する必要はありません。 配列内でインデックスが複数回現れる入力引数は複製され、配列内でインデックスが現れない入力引数は除去されます。 dropArgumentsの場合と同様に、並べ替え配列に記載されていない入力引数は、newTypeによってのみ決定されるような任意の型である可能性があります。

        
        import static java.lang.invoke.MethodHandles.*;
        import static java.lang.invoke.MethodType.*;
        ...
        MethodType intfn1 = methodType(int.class, int.class);
        MethodType intfn2 = methodType(int.class, int.class, int.class);
        MethodHandle sub = ... (int x, int y) -> (x-y) ...;
        assert(sub.type().equals(intfn2));
        MethodHandle sub1 = permuteArguments(sub, intfn2, 0, 1);
        MethodHandle rsub = permuteArguments(sub, intfn2, 1, 0);
        assert((int)rsub.invokeExact(1, 100) == 99);
        MethodHandle add = ... (int x, int y) -> (x+y) ...;
        assert(add.type().equals(intfn2));
        MethodHandle twice = permuteArguments(add, intfn1, 0, 0);
        assert(twice.type().equals(intfn1));
        assert((int)twice.invokeExact(21) == 42);
         

        ノート: たとえ元のターゲット・メソッド・ハンドルがあったとしても、得られるアダプタは決して可変メソッド・ハンドルではありません。

        パラメータ:
        target - 引数を並べ替えたあとで呼び出すメソッド・ハンドル
        newType - 新しいメソッド・ハンドルの期待される型
        reorder - 並べ替えを制御するインデックス配列
        戻り値:
        未使用の引数を破棄し、ほかの引数の移動や複製を行ったあとでターゲットに委譲するメソッド・ハンドル
        例外:
        NullPointerException - いずれかの引数がnullの場合
        IllegalArgumentException - インデックス配列の長さがターゲットの引数長と等しくない場合、またはインデックス配列に、newTypeのパラメータの有効なインデックスでない要素が含まれている場合、またはtarget.type()newTypeの対応する2つのパラメータの型が同一でない場合
      • constant

        public static MethodHandle constant​(Class<?> type,
                                            Object value)
        要求された戻り値の型を持ち、呼び出されるたびに指定された定数値を返すメソッド・ハンドルを生成します。

        メソッド・ハンドルが返される前に、渡された値が要求された型に変換されます。 要求された型がプリミティブの場合はプリミティブ・ワイドニング変換が試みられ、それ以外の場合は参照変換が試みられます。

        返されるメソッド・ハンドルはidentity(type).bindTo(value)と同等です。

        パラメータ:
        type - 必要なメソッド・ハンドルの戻り値の型
        value - 返す値
        戻り値:
        指定された戻り値の型を持ち引数は一切取らない、常に指定された値を返すメソッド・ハンドル
        例外:
        NullPointerException - type引数がnullの場合
        ClassCastException - 要求された戻り値の型に値を変換できない場合
        IllegalArgumentException - 指定された型がvoid.classの場合
      • identity

        public static MethodHandle identity​(Class<?> type)
        呼出し時に唯一の引数の値を返すメソッド・ハンドルを生成します。
        パラメータ:
        type - 必要なメソッド・ハンドルの唯一のパラメータと戻り値の型
        戻り値:
        指定された型を受け入れて返す単項メソッド・ハンドル
        例外:
        NullPointerException - 引数がnullの場合
        IllegalArgumentException - 指定された型がvoid.classの場合
      • 0

        public static MethodHandle zero​(Class<?> type)
        呼び出されるたびにその型のデフォルト値を返すリクエストされた戻り値型の定数メソッド・ハンドルを生成します。 結果の定数メソッド・ハンドルには副作用がありません。

        返されるメソッド・ハンドルは、empty(methodType(type))と等価です。 また、explicitCastArgumentsnullをデフォルト値に変換するため、explicitCastArguments(constant(Object.class, null), methodType(type))と同等です。

        パラメータ:
        type - 目的のメソッド・ハンドルの期待される戻り値の型
        戻り値:
        引数をとらず、指定された型のデフォルト値を返す定数メソッド・ハンドル(型がvoidの場合はvoid)
        例外:
        NullPointerException - 引数がnullの場合
        導入されたバージョン:
        9
        関連項目:
        constant(java.lang.Class<?>, java.lang.Object), empty(java.lang.invoke.MethodType), explicitCastArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType)
      • empty

        public static MethodHandle empty​(MethodType type)
        任意の引数を無視し、何もせず、戻り値の型に応じて適切な既定値を返す、リクエストされた型のメソッド・ハンドルを生成します。 つまり、null、またはvoidのゼロ・プリミティブ値を返します。

        返されるメソッド・ハンドルは、dropArguments(zero(type.returnType()), 0, type.parameterList())と等価です。

        APIの注:
        述部とターゲットが与えられれば、有用な"if-then"構成をguardWithTest(pred, target, empty(target.type())として生成することができます。
        パラメータ:
        type - 目的のメソッド・ハンドルの型
        戻り値:
        与えられた戻り値の型のデフォルト値を返す、指定された型の定数メソッド・ハンドル
        例外:
        NullPointerException - 引数がnullの場合
        導入されたバージョン:
        9
        関連項目:
        zero(java.lang.Class<?>), constant(java.lang.Class<?>, java.lang.Object)
      • insertArguments

        public static MethodHandle insertArguments​(MethodHandle target,
                                                   int pos,
                                                   Object... values)
        ターゲット・メソッド・ハンドルの呼出しの前に、1つ以上のバインド引数をメソッド・ハンドルに提供します。 ターゲットの仮パラメータのうちで、バインド引数に対応しているものは、バインド・パラメータと呼ばれます。 バインド引数を保存した新しいメソッド・ハンドルを返します。 これは呼出し時に、すべての非バインド・パラメータに対する引数を受け取り、保存しておいた引数を対応するパラメータにバインドし、元のターゲットを呼び出します。

        新しいメソッド・ハンドルの型には、元のターゲットの型に含まれていたバインド・パラメータの型は含まれませんが、これは、新しいメソッド・ハンドルではもう、呼出し元がそれらの引数を指定する必要がないからです。

        指定された引数のオブジェクトはそれぞれ、対応するバインド・パラメータの型に一致する必要があります。 バインド・パラメータの型がプリミティブの場合、引数のオブジェクトはラッパーである必要があり、オブジェクトがアンボクシングされてプリミティブ値が生成されます。

        pos引数によってバインドするパラメータが選択されます。 その範囲は0 - N-L (両端を含む)です。ここで、Nはターゲット・メソッド・ハンドルの引数長、Lは値配列の長さです。

        ノート: たとえ元のターゲット・メソッド・ハンドルがあったとしても、得られるアダプタは決して可変メソッド・ハンドルではありません。

        パラメータ:
        target - 引数を挿入したあとに呼び出すメソッド・ハンドル
        pos - 引数の挿入位置(先頭の場合はゼロ)
        values - 挿入する一連の引数
        戻り値:
        追加の引数を挿入してから元のメソッド・ハンドルを呼び出すメソッド・ハンドル
        例外:
        NullPointerException - ターゲットまたはvalues配列がnullの場合
        関連項目:
        MethodHandle.bindTo(java.lang.Object)
      • dropArguments

        public static MethodHandle dropArguments​(MethodHandle target,
                                                 int pos,
                                                 List<Class<?>> valueTypes)
        いくつかのダミー引数を破棄してから指定された別のtargetメソッド・ハンドルを呼び出すメソッド・ハンドルを生成します。 新しいメソッド・ハンドルの型は、ターゲットの型とほぼ同じになりますが、指定された位置にダミー引数の型も含む点が異なります。

        pos引数の範囲は0 - N (Nはターゲットの引数の数)になります。 ダミー引数は、posが0の場合はターゲットの実際の引数の前に追加され、posNの場合は後ろに追加されます。

        例:

        
        import static java.lang.invoke.MethodHandles.*;
        import static java.lang.invoke.MethodType.*;
        ...
        MethodHandle cat = lookup().findVirtual(String.class,
          "concat", methodType(String.class, String.class));
        assertEquals("xy", (String) cat.invokeExact("x", "y"));
        MethodType bigType = cat.type().insertParameterTypes(0, int.class, String.class);
        MethodHandle d0 = dropArguments(cat, 0, bigType.parameterList().subList(0,2));
        assertEquals(bigType, d0.type());
        assertEquals("yz", (String) d0.invokeExact(123, "x", "y", "z"));
         

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

         dropArguments(target, pos, valueTypes.toArray(new Class[0]))
         
        パラメータ:
        target - 引数を除去したあとに呼び出すメソッド・ハンドル
        valueTypes - 除去する引数の型
        pos - 除去する最初の引数の位置(左端の場合は0)
        戻り値:
        指定された型の引数を除去してから元のメソッド・ハンドルを呼び出すメソッド・ハンドル
        例外:
        NullPointerException - ターゲットがnullの場合、またはvalueTypesリストまたはそのいずれかの要素がnullの場合
        IllegalArgumentException - valueTypesのいずれかの要素がvoid.classの場合、またはposが負であるかターゲットの引数の数より大きい場合、または新しいメソッド・ハンドルの型に含まれるパラメータの数が多すぎる場合
      • dropArguments

        public static MethodHandle dropArguments​(MethodHandle target,
                                                 int pos,
                                                 Class<?>... valueTypes)
        いくつかのダミー引数を破棄してから指定された別のtargetメソッド・ハンドルを呼び出すメソッド・ハンドルを生成します。 新しいメソッド・ハンドルの型は、ターゲットの型とほぼ同じになりますが、指定された位置にダミー引数の型も含む点が異なります。

        pos引数の範囲は0 - N (Nはターゲットの引数の数)になります。 ダミー引数は、posが0の場合はターゲットの実際の引数の前に追加され、posNの場合は後ろに追加されます。

        APIの注:
        
        import static java.lang.invoke.MethodHandles.*;
        import static java.lang.invoke.MethodType.*;
        ...
        MethodHandle cat = lookup().findVirtual(String.class,
          "concat", methodType(String.class, String.class));
        assertEquals("xy", (String) cat.invokeExact("x", "y"));
        MethodHandle d0 = dropArguments(cat, 0, String.class);
        assertEquals("yz", (String) d0.invokeExact("x", "y", "z"));
        MethodHandle d1 = dropArguments(cat, 1, String.class);
        assertEquals("xz", (String) d1.invokeExact("x", "y", "z"));
        MethodHandle d2 = dropArguments(cat, 2, String.class);
        assertEquals("xy", (String) d2.invokeExact("x", "y", "z"));
        MethodHandle d12 = dropArguments(cat, 1, int.class, boolean.class);
        assertEquals("xz", (String) d12.invokeExact("x", 12, true, "z"));
         

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

         dropArguments(target, pos, Arrays.asList(valueTypes))
         
        パラメータ:
        target - 引数を除去したあとに呼び出すメソッド・ハンドル
        valueTypes - 除去する引数の型
        pos - 除去する最初の引数の位置(左端の場合は0)
        戻り値:
        指定された型の引数を除去してから元のメソッド・ハンドルを呼び出すメソッド・ハンドル
        例外:
        NullPointerException - ターゲットがnullの場合、またはvalueTypes配列またはそのいずれかの要素がnullの場合
        IllegalArgumentException - valueTypesのいずれかの要素がvoid.classの場合、またはposが負またはターゲットの引数カウントより大きい場合、または新しいメソッド・ハンドルの型のパラメータ数が多すぎる場合
      • dropArgumentsToMatch

        public static MethodHandle dropArgumentsToMatch​(MethodHandle target,
                                                        int skip,
                                                        List<Class<?>> newTypes,
                                                        int pos)
        指定されたパラメータ型に一致するように、ターゲット・メソッド・ハンドルを適合させます。 必要に応じて仮引数を追加します。 いくつかの先行パラメータは、マッチングが始まる前にスキップすることができます。 targetパラメータ型リストの残りの型は、開始位置posnewTypes型リストのサブ・リストでなければなりません。 結果ハンドルには、ターゲット・ハンドル・パラメータ・タイプ・リストがあり、一致しないパラメータ・タイプ(一致するサブ・リストの前後に)がターゲット・オリジナル・パラメータの対応する位置にdropArguments(MethodHandle, int, Class[])のように挿入されます。

        結果のハンドルは、ターゲット・ハンドルと同じ戻り型を持ちます。

        もっと正式な言い方をすれば、次の2つの型のリストを想定してください:

        • ターゲット・ハンドルは、skipで示されるように、Sに多くの型を持つパラメータ型リストS..., M...を持っています。 M型は、指定された型リストnewTypesの一部と一致するはずの型です。
        • newTypesリストには、posで示されるように、Pに多くの型が含まれるP..., M..., A...型が含まれています。 M型は、ターゲット・ハンドル・パラメータ型リストのM型が一致すると想定されるものとまったく同じです。 Aの型は、一致するサブ・リストの後に見つかった追加型です。
        これらの前提を前提にすると、dropArgumentsToMatchの呼び出しの結果は、PAの型がdropArguments(MethodHandle, int, Class[])のように挿入されたパラメータ型リストS..., P..., M..., A...を持ちます。

        APIの注:
        引数リストが"効果的に同一" (すなわち、共通接頭語で同一である)である2つのメソッド・ハンドルは、次のように、dropArgumentsToMatchへの2回の呼び出しによって共通の型に相互変換できます:
        
        import static java.lang.invoke.MethodHandles.*;
        import static java.lang.invoke.MethodType.*;
        ...
        ...
        MethodHandle h0 = constant(boolean.class, true);
        MethodHandle h1 = lookup().findVirtual(String.class, "concat", methodType(String.class, String.class));
        MethodType bigType = h1.type().insertParameterTypes(1, String.class, int.class);
        MethodHandle h2 = dropArguments(h1, 0, bigType.parameterList());
        if (h1.type().parameterCount() < h2.type().parameterCount())
            h1 = dropArgumentsToMatch(h1, 0, h2.type().parameterList(), 0);  // lengthen h1
        else
            h2 = dropArgumentsToMatch(h2, 0, h1.type().parameterList(), 0);    // lengthen h2
        MethodHandle h3 = guardWithTest(h0, h1, h2);
        assertEquals("xy", h3.invoke("x", "y", 1, "a", "b", "c"));
         
        パラメータ:
        target - 適応するメソッド・ハンドル
        skip - (彼らは変わらないでしょう)を無視するターゲット・パラメータの数
        newTypes - targetパラメータの型リストに一致する型のリスト
        pos - スキップされていないターゲット・パラメータが発生しなければならないnewTypesに配置
        戻り値:
        場合によっては適合したメソッド・ハンドル
        例外:
        NullPointerException - どちらかの引数がnullの場合
        IllegalArgumentException - newTypesのいずれかの要素がvoid.classである場合、またはskipが負であるか、ターゲットのアリティより大きい場合、またはposが負であるかnewTypesリスト・サイズより大きい場合、またはnewTypestargetが含まれない場合pos
        導入されたバージョン:
        9
      • filterArguments

        public static MethodHandle filterArguments​(MethodHandle target,
                                                   int pos,
                                                   MethodHandle... filters)
        ターゲット・メソッド・ハンドルを適応させるため、その1つ以上の引数をそれぞれ固有の単項フィルタ関数を使って前処理したあと、前処理を行った各引数を対応するフィルタ関数の結果で置き換えてターゲットを呼び出します。

        前処理は、filters配列の要素として指定された1つ以上のメソッド・ハンドルによって実行されます。 フィルタ配列の最初の要素がターゲットのposの位置の引数に対応する、といった関係がその後も順に続きます。

        配列内のnull引数はアイデンティティ関数とみなされ、対応する引数は変更されないままになります。 (nullでない要素が配列内に1つも存在しない場合は、元のターゲットが返されます。) 各フィルタはアダプタの対応する引数に適用されます。

        フィルタFがターゲットのN番目の引数に適用される場合、Fは、ちょうど1つの引数を取るメソッド・ハンドルでなければいけません。 結果となる適応後のメソッド・ハンドル内では、Fの唯一の引数の型で、ターゲットの対応する引数の型が置き換えられます。 Fの戻り値の型は、ターゲットの対応するパラメータの型と同一でなければいけません。

        ターゲット内の引数位置に対応しないfiltersの要素(nullの場合がある)が存在する場合は、エラーになります。

        例:

        
        import static java.lang.invoke.MethodHandles.*;
        import static java.lang.invoke.MethodType.*;
        ...
        MethodHandle cat = lookup().findVirtual(String.class,
          "concat", methodType(String.class, String.class));
        MethodHandle upcase = lookup().findVirtual(String.class,
          "toUpperCase", methodType(String.class));
        assertEquals("xy", (String) cat.invokeExact("x", "y"));
        MethodHandle f0 = filterArguments(cat, 0, upcase);
        assertEquals("Xy", (String) f0.invokeExact("x", "y")); // Xy
        MethodHandle f1 = filterArguments(cat, 1, upcase);
        assertEquals("xY", (String) f1.invokeExact("x", "y")); // xY
        MethodHandle f2 = filterArguments(cat, 0, upcase, upcase);
        assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY
         

        結果のアダプタの擬似コードは次のとおりです。 コードでは、Tは、targetとその結果のアダプタの両方の戻り型を示します。 P/pおよびB/bは、それぞれフィルタ位置posの前および後のパラメータおよび引数の型および値を表す。 A[i]/a[i]は、フィルタリングされたパラメータと引数の型と値を表します。filter[i]ハンドルの戻り値の型も表します。 後者は、型V[i]の引数v[i]を受け取ります。V[i]は、結果のアダプタのシグネチャにも現れます。

        
         T target(P... p, A[i]... a[i], B... b);
         A[i] filter[i](V[i]);
         T adapter(P... p, V[i]... v[i], B... b) {
           return target(p..., filter[i](v[i])..., b...);
         }
         

        ノート: たとえ元のターゲット・メソッド・ハンドルがあったとしても、得られるアダプタは決して可変メソッド・ハンドルではありません。

        パラメータ:
        target - 引数をフィルタリングしたあとで呼び出すメソッド・ハンドル
        pos - フィルタリングする最初の引数の位置
        filters - フィルタリング対象の引数に対して最初に呼び出すメソッド・ハンドル
        戻り値:
        指定された引数フィルタリング・ロジックが組み込まれたメソッド・ハンドル
        例外:
        NullPointerException - ターゲットがnullの場合またはfilters配列がnullの場合
        IllegalArgumentException - filtersの非null要素がターゲットの対応する引数型と一致しない場合(前述の説明を参照)、またはpos+filters.lengthtarget.type().parameterCount()より大きい場合、または結果のメソッド・ハンドルの型のパラメータ数が多すぎる場合
      • collectArguments

        public static MethodHandle collectArguments​(MethodHandle target,
                                                    int pos,
                                                    MethodHandle filter)
        ターゲット・メソッド・ハンドルを、フィルタ(別のメソッド・ハンドル)でその引数のサブシーケンスを前処理することにより、適応させます。 前処理した引数は、フィルタ関数の結果(ある場合)によって置き換えられます。 すると、ターゲットは変更された(通常は短縮された)引数リストで呼び出されます。

        フィルタが値を返す場合、ターゲットはその値を位置pos(フィルタに渡されない引数の前または後)の引数として受け入れる必要があります。 フィルタがvoidを返す場合、ターゲットはフィルタに渡されないすべての引数を受け入れる必要があります。 引数は並べ替えられず、フィルタから返された結果によってアダプタにもともと渡された引数サブシーケンス全体が(順番に)置き換えられます。

        フィルタの引数型(ある場合)は、結果の適応済メソッド・ハンドル内で、ターゲットの0または1個の引数型(位置pos)を置き換えます。 フィルタの戻り型(ある場合)は、ターゲットの位置posの引数型と同じである必要があり、そのターゲット引数はフィルタの戻り値によって提供されます。

        どのような場合でも、posは0以上である必要があり、posはターゲットの引数カウント以下である必要があります。

        例:

        
        import static java.lang.invoke.MethodHandles.*;
        import static java.lang.invoke.MethodType.*;
        ...
        MethodHandle deepToString = publicLookup()
          .findStatic(Arrays.class, "deepToString", methodType(String.class, Object[].class));
        
        MethodHandle ts1 = deepToString.asCollector(String[].class, 1);
        assertEquals("[strange]", (String) ts1.invokeExact("strange"));
        
        MethodHandle ts2 = deepToString.asCollector(String[].class, 2);
        assertEquals("[up, down]", (String) ts2.invokeExact("up", "down"));
        
        MethodHandle ts3 = deepToString.asCollector(String[].class, 3);
        MethodHandle ts3_ts2 = collectArguments(ts3, 1, ts2);
        assertEquals("[top, [up, down], strange]",
                     (String) ts3_ts2.invokeExact("top", "up", "down", "strange"));
        
        MethodHandle ts3_ts2_ts1 = collectArguments(ts3_ts2, 3, ts1);
        assertEquals("[top, [up, down], [strange]]",
                     (String) ts3_ts2_ts1.invokeExact("top", "up", "down", "strange"));
        
        MethodHandle ts3_ts2_ts3 = collectArguments(ts3_ts2, 1, ts3);
        assertEquals("[top, [[up, down, strange], charm], bottom]",
                     (String) ts3_ts2_ts3.invokeExact("top", "up", "down", "strange", "charm", "bottom"));
         

        結果のアダプタの擬似コードは次のとおりです。 コードでは、Ttargetの戻り値の型とその結果のアダプタを表します。 V/vは、Vvoidでないかぎり、それぞれtargetのシグネチャと引数にあるfilterの戻り値の型と値を表します。 A/aおよびC/cは、targetシグネチャ内の収集位置posの前後のパラメータ型および値を表します。 また、filter (もしあれば)のパラメータの型と引数を表す、B/bを囲むアダプタのシグネチャと引数が生成されます。

        
         T target(A...,V,C...);
         V filter(B...);
         T adapter(A... a,B... b,C... c) {
           V v = filter(b...);
           return target(a...,v,c...);
         }
         // and if the filter has no arguments:
         T target2(A...,V,C...);
         V filter2();
         T adapter2(A... a,C... c) {
           V v = filter2();
           return target2(a...,v,c...);
         }
         // and if the filter has a void return:
         T target3(A...,C...);
         void filter3(B...);
         T adapter3(A... a,B... b,C... c) {
           filter3(b...);
           return target3(a...,c...);
         }
         

        コレクション・アダプタcollectArguments(mh, 0, coll)は、次のような個別のステップで、まず影響を受ける引数を折りたたんでから(fold)破棄(drop)するものと同等です。

        
         mh = MethodHandles.dropArguments(mh, 1, coll.type().parameterList()); //step 2
         mh = MethodHandles.foldArguments(mh, coll); //step 1
         
        ターゲット・メソッド・ハンドルがフィルタcollの結果(ある場合)以外に引数を消費しない場合は、collectArguments(mh, 0, coll)filterReturnValue(coll, mh)と同等です。 フィルタ・メソッド・ハンドルcollが1つの引数を消費して非void結果を生成する場合は、collectArguments(mh, N, coll)filterArguments(mh, N, coll)と同等です。 他の等価性も可能ですが、引数順列が必要です。

        ノート: たとえ元のターゲット・メソッド・ハンドルがあったとしても、得られるアダプタは決して可変メソッド・ハンドルではありません。

        パラメータ:
        target - 引数サブシーケンスをフィルタした後に呼び出すメソッド・ハンドル
        pos - フィルタに渡す最初のアダプタ引数、またはフィルタの結果を受け取るターゲット引数、あるいはその両方の位置
        filter - 引数サブシーケンスで呼び出すメソッド・ハンドル
        戻り値:
        指定された引数サブシーケンス・フィルタリング・ロジックを組み込んだメソッド・ハンドル
        例外:
        NullPointerException - どちらかの引数がnullの場合
        IllegalArgumentException - filterの戻り型が非voidでターゲットのpos引数と同じでない場合、またはposが0からターゲットの引数カウントの間(含む)にない場合、または結果のメソッド・ハンドルの型のパラメータ数が多すぎる場合
        関連項目:
        foldArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle)filterArguments(java.lang.invoke.MethodHandle, int, java.lang.invoke.MethodHandle...)filterReturnValue(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle)
      • filterReturnValue

        public static MethodHandle filterReturnValue​(MethodHandle target,
                                                     MethodHandle filter)
        ターゲット・メソッド・ハンドルを適応させるため、その戻り値(存在する場合)をフィルタ(別のメソッド・ハンドル)で後処理します。 フィルタの結果がアダプタから返されます。

        ターゲットが値を返す場合、フィルタはその値を唯一の引数として受け入れる必要があります。 ターゲットがvoidを返す場合、フィルタは引数を一切受け入れてはいけません。

        結果となる適応後のメソッド・ハンドル内では、フィルタの戻り値の型でターゲットの戻り値の型が置き換えられます。 フィルタの引数の型(存在する場合)は、ターゲットの戻り値の型と同一でなければいけません。

        例:

        
        import static java.lang.invoke.MethodHandles.*;
        import static java.lang.invoke.MethodType.*;
        ...
        MethodHandle cat = lookup().findVirtual(String.class,
          "concat", methodType(String.class, String.class));
        MethodHandle length = lookup().findVirtual(String.class,
          "length", methodType(int.class));
        System.out.println((String) cat.invokeExact("x", "y")); // xy
        MethodHandle f0 = filterReturnValue(cat, length);
        System.out.println((int) f0.invokeExact("x", "y")); // 2
         

        結果のアダプタの擬似コードは次のとおりです。 コードでは、T/ttargetの結果の型と値を表します。Vfilterの結果型。A/atargetのパラメータと引数の型と値、および結果のアダプタ。

        
         T target(A...);
         V filter(T);
         V adapter(A... a) {
           T t = target(a...);
           return filter(t);
         }
         // and if the target has a void return:
         void target2(A...);
         V filter2();
         V adapter2(A... a) {
           target2(a...);
           return filter2();
         }
         // and if the filter has a void return:
         T target3(A...);
         void filter3(V);
         void adapter3(A... a) {
           T t = target3(a...);
           filter3(t);
         }
         

        ノート: たとえ元のターゲット・メソッド・ハンドルがあったとしても、得られるアダプタは決して可変メソッド・ハンドルではありません。

        パラメータ:
        target - 戻り値をフィルタリングする前に呼び出すメソッド・ハンドル
        filter - 戻り値に対して呼び出すメソッド・ハンドル
        戻り値:
        指定された戻り値フィルタリング・ロジックが組み込まれたメソッド・ハンドル
        例外:
        NullPointerException - どちらかの引数がnullの場合
        IllegalArgumentException - 前述のようにfilterの引数リストがターゲットの戻り値の型と一致しない場合
      • foldArguments

        public static MethodHandle foldArguments​(MethodHandle target,
                                                 MethodHandle combiner)
        ターゲット・メソッド・ハンドルを適応させるため、その引数のいくつかを前処理したあと、前処理の結果を元の一連の引数内に挿入してターゲットを呼び出します。

        前処理は、2番目のメソッド・ハンドルであるcombinerによって実行されます。 アダプタに渡された引数のうち、最初のN個の引数がコンバイナにコピーされたあと、コンバイナが呼び出されます。 (ここで、Nはコンバイナのパラメータ数として定義される。) このあと制御がターゲットに渡されますが、その際、コンバイナのすべての結果が元のN個の入力引数の前に挿入されます。

        コンバイナが値を返す場合、ターゲットの最初のパラメータの型がコンバイナの戻り値の型と同一である必要があるほか、ターゲットの次のN個のパラメータの型がコンバイナのパラメータと厳密に一致している必要があります。

        コンバイナの戻り値がvoidの場合、結果は一切挿入されないので、ターゲットの最初のN個のパラメータの型がコンバイナのパラメータと厳密に一致する必要があります。

        結果となるアダプタの型はターゲットとほぼ同じになりますが、最初のパラメータの型がコンバイナの結果に対応している場合はその型は除去される点が異なります。

        (dropArgumentsを使えば、コンバイナまたはターゲットが受け取る必要のない引数をすべて削除できます。 入力引数の一部がコンバイナ専用の場合、それらの引数はターゲットへのエントリ時にスタック上に存在している必要がないため、asCollectorを代わりに使用することを検討してください。)

        例:

        
        import static java.lang.invoke.MethodHandles.*;
        import static java.lang.invoke.MethodType.*;
        ...
        MethodHandle trace = publicLookup().findVirtual(java.io.PrintStream.class,
          "println", methodType(void.class, String.class))
            .bindTo(System.out);
        MethodHandle cat = lookup().findVirtual(String.class,
          "concat", methodType(String.class, String.class));
        assertEquals("boojum", (String) cat.invokeExact("boo", "jum"));
        MethodHandle catTrace = foldArguments(cat, trace);
        // also prints "boo":
        assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
         

        結果のアダプタの擬似コードは次のとおりです。 コードでは、Ttargetの結果の型とその結果のアダプタを表します。 V/vは、折り返し位置に先行するtargetのパラメータと引数の型と値を表します。Vcombinerの結果型です。 A/aは、折り畳み位置でのNパラメータと引数の型と値を示します。 B/bは、targetのパラメータと引数のタイプと値を表し、折り返されたパラメータと引数に続くものです。

        
         // there are N arguments in A...
         T target(V, A[N]..., B...);
         V combiner(A...);
         T adapter(A... a, B... b) {
           V v = combiner(a...);
           return target(v, a..., b...);
         }
         // and if the combiner has a void return:
         T target2(A[N]..., B...);
         void combiner2(A...);
         T adapter2(A... a, B... b) {
           combiner2(a...);
           return target2(a..., b...);
         }
         

        ノート: たとえ元のターゲット・メソッド・ハンドルがあったとしても、得られるアダプタは決して可変メソッド・ハンドルではありません。

        パラメータ:
        target - 引数を結合したあとで呼び出すメソッド・ハンドル
        combiner - 入力引数に対して最初に呼び出すメソッド・ハンドル
        戻り値:
        指定された引数フォールディング・ロジックが組み込まれたメソッド・ハンドル
        例外:
        NullPointerException - どちらかの引数がnullの場合
        IllegalArgumentException - combinerの戻り値の型がvoidでなく、ターゲットの最初の引数の型と同じでない場合、あるいはターゲットの最初のN個の引数の型(combinerの戻り値の型に一致する型は除く)がcombinerの引数の型と同一でない場合
      • foldArguments

        public static MethodHandle foldArguments​(MethodHandle target,
                                                 int pos,
                                                 MethodHandle combiner)
        指定された位置から開始し、前処理の結果でターゲットを呼び出し、折りたたまれた引数の直前の引数の元のシーケンスに挿入して、引数の一部を前処理することによって、ターゲット・メソッド・ハンドルを修正します。

        このメソッドはfoldArguments(MethodHandle, MethodHandle)と密接に関連していますが、折り畳みが行われるパラメータ・リスト内の位置を制御することができます。 これを制御する引数posはゼロ・ベースのインデックスです。 前述のメソッドfoldArguments(MethodHandle, MethodHandle)は位置0を取る。

        APIの注:
        例:
        
            import static java.lang.invoke.MethodHandles.*;
            import static java.lang.invoke.MethodType.*;
            ...
            MethodHandle trace = publicLookup().findVirtual(java.io.PrintStream.class,
            "println", methodType(void.class, String.class))
            .bindTo(System.out);
            MethodHandle cat = lookup().findVirtual(String.class,
            "concat", methodType(String.class, String.class));
            assertEquals("boojum", (String) cat.invokeExact("boo", "jum"));
            MethodHandle catTrace = foldArguments(cat, 1, trace);
            // also prints "jum":
            assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
         

        結果のアダプタの擬似コードは次のとおりです。 コードでは、Ttargetの結果の型とその結果のアダプタを表します。 V/vは、折り返し位置に先行するtargetのパラメータと引数の型と値を表します。Vcombinerの結果型です。 A/aは、折り畳み位置でのNパラメータと引数の型と値を示します。 Z/zB/bは、それぞれposで始まる折り畳まれたパラメータと引数の前後にあるtargetパラメータと引数の型と値を表します。

        
         // there are N arguments in A...
         T target(Z..., V, A[N]..., B...);
         V combiner(A...);
         T adapter(Z... z, A... a, B... b) {
           V v = combiner(a...);
           return target(z..., v, a..., b...);
         }
         // and if the combiner has a void return:
         T target2(Z..., A[N]..., B...);
         void combiner2(A...);
         T adapter2(Z... z, A... a, B... b) {
           combiner2(a...);
           return target2(z..., a..., b...);
         }
         

        ノート: たとえ元のターゲット・メソッド・ハンドルがあったとしても、得られるアダプタは決して可変メソッド・ハンドルではありません。

        パラメータ:
        target - 引数を結合したあとで呼び出すメソッド・ハンドル
        pos - 折りたたみを開始し、折り畳み結果を挿入する位置。これが 0の場合、効果はfoldArguments(MethodHandle, MethodHandle)と同じです。
        combiner - 入力引数に対して最初に呼び出すメソッド・ハンドル
        戻り値:
        指定された引数フォールディング・ロジックが組み込まれたメソッド・ハンドル
        例外:
        NullPointerException - どちらかの引数がnullの場合
        IllegalArgumentException - 以下の2つの条件のいずれかが成り立つ: (1) combiner戻り型がvoidではなく、ターゲット・シグネチャの位置posの引数の型と同じではありません。(2)ターゲット・シグネチャ(combiner戻り値の型と一致するものをスキップ)のposの位置にあるN引数の型は、引数の型combinerと同じではありません。
        導入されたバージョン:
        9
        関連項目:
        foldArguments(MethodHandle, MethodHandle)
      • guardWithTest

        public static MethodHandle guardWithTest​(MethodHandle test,
                                                 MethodHandle target,
                                                 MethodHandle fallback)
        テスト(boolean値のメソッド・ハンドル)で保護することでターゲット・メソッド・ハンドルを適応させるメソッド・ハンドルを作成します。 保護に失敗した場合は、代わりにフォール・バック・ハンドルが呼び出されます。 テストの戻り値の型がbooleanでなければいけない点と、テストの引数の数がほかの2つのメソッド・ハンドルより少なくてもかまわないという点を除き、3つのメソッド・ハンドルの対応する引数と戻り値の型はすべて同じである必要があります。

        結果のアダプタの擬似コードは次のとおりです。 コードでは、Tは、3つのハンドルの統一された結果の型を表します。A/atestで消費されるtargetパラメータと引数の型と値。B/bと、testによって消費されないtargetパラメータと引数の型と値。

        
         boolean test(A...);
         T target(A...,B...);
         T fallback(A...,B...);
         T adapter(A... a,B... b) {
           if (test(a...))
             return target(a..., b...);
           else
             return fallback(a..., b...);
         }
         
        テストの引数(擬似コードではa...)は、テストの実行時に変更される可能性はないので、呼出し元から必要に応じてターゲットまたはフォール・バックにそのまま渡されます。
        パラメータ:
        test - テストに使用されるメソッド・ハンドルでbooleanを返す必要がある
        target - テストにパスした場合に呼び出すメソッド・ハンドル
        fallback - テストに失敗した場合に呼び出すメソッド・ハンドル
        戻り値:
        指定されたif/then/elseロジックが組み込まれたメソッド・ハンドル
        例外:
        NullPointerException - いずれかの引数がnullの場合
        IllegalArgumentException - testがbooleanを返さない場合、または(testの戻り値の型を変更してターゲットの型と一致させても) 3つのすべてのメソッド型が一致しない場合
      • catchException

        public static MethodHandle catchException​(MethodHandle target,
                                                  Class<? extends Throwable> exType,
                                                  MethodHandle handler)
        ターゲットのメソッド・ハンドルを例外ハンドラの内部で実行することによって、このターゲットを適応させるメソッド・ハンドルを作成します。 ターゲットが通常どおり値を返した場合、アダプタはその値を返します。 指定された型に一致する例外がスローされた場合、代わりにその例外と元の引数を指定してフォール・バック・ハンドルが呼び出されます。

        ターゲットとハンドラの対応する引数と戻り値の型は基本的に同じである必要がありますが、ハンドラでは(guardWithTestの述語と同様に)末尾の複数の引数を省略できます。 さらにハンドラは、exTypeまたはスーパー・タイプの先頭のパラメータを追加で1つ持つ必要があります。

        結果のアダプタの擬似コードは次のとおりです。 コードでは、Ttargethandlerの戻り値の型を表し、それに対応して結果のアダプタの戻り値の型も表します。A/ahandlerが消費する結果ハンドルの引数の型と値。B/bhandlerによって破棄された結果のハンドルへの引数のもの。

        
         T target(A..., B...);
         T handler(ExType, A...);
         T adapter(A... a, B... b) {
           try {
             return target(a..., b...);
           } catch (ExType ex) {
             return handler(ex, a...);
           }
         }
         
        保存された引数(擬似コードではa...)は、ターゲットの実行時に変更される可能性はないので、ハンドラが呼び出される場合には呼出し元からハンドラにそのまま渡されます。

        ハンドラが常にスローする場合でも、ターゲットとハンドラの戻り値の型は同じでなければいけません。 (これは、たとえばハンドラがfinally節をシミュレートしているために発生する可能性があります)。 そのようなスローするハンドラを作成するには、throwExceptionを使ってハンドラ作成ロジックを構築し、正しい戻り値の型を持つメソッド・ハンドルが作成されるようにします。

        パラメータ:
        target - 呼び出すメソッド・ハンドル
        exType - ハンドラがキャッチする例外の型
        handler - 一致する例外がスローされた場合に呼び出すメソッド・ハンドル
        戻り値:
        指定されたtry/catchロジックが組み込まれたメソッド・ハンドル
        例外:
        NullPointerException - いずれかの引数がnullの場合
        IllegalArgumentException - handlerが指定された例外の型を受け入れない場合、またはメソッド・ハンドルの型に含まれる戻り値の型と対応するパラメータが一致しない場合
        関連項目:
        tryFinally(MethodHandle, MethodHandle)
      • throwException

        public static MethodHandle throwException​(Class<?> returnType,
                                                  Class<? extends Throwable> exType)
        指定されたexTypeの例外をスローするメソッド・ハンドルを生成します。 メソッド・ハンドルは、exTypeの単一の引数を受け入れ、それを即時に例外としてスローします。 メソッド型では形式上、returnTypeの戻り値が指定されます。 戻り値の型は、どのようなものでもかまいません。メソッド・ハンドルが通常どおりに戻ることは決してないので、メソッド・ハンドルの動作には何の影響もありません。
        パラメータ:
        returnType - 期待するメソッド・ハンドルの戻り型
        exType - 期待するメソッド・ハンドルのパラメータ型
        戻り値:
        指定された例外をスローできるメソッド・ハンドル
        例外:
        NullPointerException - どちらかの引数がnullの場合
      • loop

        public static MethodHandle loop​(MethodHandle[]... clauses)
        各反復時に更新およびチェックされる複数のループ変数を持つループを表すメソッド・ハンドルを作成します。 述語の1つのためにループが終了すると、対応するファイナライザが実行され、結果のハンドルの戻り値であるループ結果が送られます。

        直感的に、すべてのループは1つまたは複数の"clauses"によって構成され、それぞれローカルの「反復変数」および/またはループ出口を指定します。 ループの各反復では、各句が順番に実行されます。 句は、反復変数を任意に更新できます。オプションでテストおよび条件付きループ出口を実行することもできます。 このロジックをメソッド・ハンドルの観点から表現するために、各句では最大4つの独立したアクションを指定します:

        • init: ループが実行される前に、型Vの反復変数vの初期化。
        • ステップ: 句が実行されると、反復変数vの更新ステップが実行されます。
        • pred: 句が実行されると、ループ終了をテストする述語実行。
        • fini: 句がループ終了を起こすと、ループ戻り値をコンピュートするためのファイナライザ実行が行われます。
        すべての反復変数型の完全なシーケンス(句の順序)は、(V...)と表記されます。 値自体は(v...)になります。 "パラメータ・リスト"について言及するとき、通常は型を参照しますが、いくつかのコンテキスト(実行を記述)では、実際の値がリストに含まれることがあります。

        これらの句部分のいくつかは、ある規則に従って省略されてもよく、この場合、有用なデフォルト動作が提供される。 詳細な説明は以下を参照してください。

        どこでもオプションのパラメータ: 各句機能は許可されていますが、各反復変数vのパラメータを受け入れる必要はありません。 例外として、init関数は、init関数が実行されたときにまだ計算されていないため、vパラメータを取ることはできません。 どの句関数も、それが取ることができるパラメータの末尾の部分列を取り除くことがあります。 実際、どの句関数も引数を全く取らない可能性があります。

        ループ・パラメータ: 句関数は、それが与えられたすべての反復変数値を取るかもしれません。その場合、より多くの後続のパラメータを取るかもしれません。 このような余分な値は、「ループ・パラメータ」と呼ばれ、その型と値は(A...)(a...)と表記されています。 これらは、ループが実行されるたびに供給される結果のループ・ハンドルのパラメータになります。 (init関数は反復変数vを受け入れないので、init関数のどのパラメータも自動的にループ・パラメータaです。) 反復変数と同様に、句関数は許可されますが、ループ・パラメータを受け入れる必要はありません。 これらのループ・パラメータは、ループ全体にわたって可視のループ不変値として機能します。

        どこでも表示できるパラメータ: 各非init句関数は、現在の反復変数値と着信ループ・パラメータの完全なリスト(v... a...)を渡すことができるため、ループ状態全体を監視することができます。 init関数は、(a...)の形式で、初期のループ状態を観察することができます。 ほとんどの句関数はこの情報のすべてを必要としませんが、dropArguments(java.lang.invoke.MethodHandle, int, java.util.List<java.lang.Class<?>>)のように正式に接続されます。 具体的には、(V*)という表記を使用して、フル・シーケンス(V...) (同様に、(v*)(A*)(a*))の任意のプレフィクスを表現します。 その表記法では、init関数のパラメータ・リストの一般形式は(A*)であり、非init関数のパラメータ・リストの一般形式は(V*)または(V... A*)です。

        チェック句の構造: 一連の句がある場合、ループのすべての部分を接続するために多数のチェックと調整が行われます。 それらは以下のステップで詳細に説明されています。 これらのステップでは、必要な制約がループ・コンビネータへの入力によって満たされない場合、単語のあらゆる出現は、IllegalArgumentExceptionがスローされる場所に対応する必要があります。

        効果的に同一の配列: ABが同一である場合、またはAがより短く、適切なプレフィクスBと同一である場合、パラメータ・リストAは、別のパラメータ・リストBに対して効果的に同一となるように定義されています。 パラメータ・リストの順序付けられていないセットについて言えば、セットが最長リストを含み、セットのすべてのメンバーがその最長リストと事実上同一である場合、セットは全体として"効果的に同一"であると言う。 たとえば、(V*)という形式の型シーケンスのセットは事実上同一であり、(V... A*)という形式のシーケンスが追加される場合も同じです。

        ステップ0: 句の構造を決定します。

        1. 句(型MethodHandle[][]の)は非nullで、少なくとも1つの要素を含んでいなければなりません。
        2. 句の配列には、nullまたは4つの要素より長いサブ配列を含めることはできません。
        3. 4つの要素よりも短い句は、null要素によって長さ4にパッディングされているかのように扱われます。 パディングは配列に要素を追加することによって行われます。
        4. すべてのnullを持つ句は無視されます。
        5. 各句は、"init"、"step"、"pred"、および"fini"という4つの関数のタプルとして扱われます。

        ステップ1A: 繰り返し変数の型(V...)を決定します。

        1. 各句の繰り返し変数の型は、句のinitおよびstepの戻り値の型を使用して決定されます。
        2. 両方の関数を省略すると、対応する句(voidがその型を示すために使用されます。)の反復変数はありません。 そのうちの1つを省略すると、もう1つの戻り値の型は句の反復変数型を定義します。 両方が指定された場合、共通の戻り値の型(彼らは同一でなければならない)は句の反復変数型を定義します。
        3. voidのすべての出現を省略して、戻り値の型(in句の順)のリストを作成します。
        4. この型のリストは、"反復変数型" ((V...))と呼ばれます。

        ステップ1B: ループ・パラメータ(A...)を決定します。

        • init関数のパラメータ・リスト(これらは(A*)の形式です)を調べて収集します。
        • 反復変数の型を削除した後、step、pred、およびfiniパラメータ・リストのサフィクスを調べて収集します。 (彼らは形式(V... A*)を持っている必要があります。(A*)部品のみを回収してください。)
        • すべての反復変数型で始まらないstep、pred、およびfiniパラメータ・リストからサフィクスを収集しないでください。 (これらの型は、ステップ2ですべての句関数型とともにチェックされます。)
        • 省略された句の関数は無視されます。 (同様に、空のパラメータ・リストを持つとみなされます。)
        • すべての収集されたパラメータ・リストは、実質的に同一でなければなりません。
        • 最も長いパラメータ・リスト(必然的にユニークです)は、"外部パラメータ・リスト" ((A...))と呼ばれます。
        • このようなパラメータ・リストがない場合、外部パラメータ・リストは空のシーケンスとみなされます。
        • 反復変数型とそれに続く外部パラメータ型の組み合わせリストは、"内部パラメータ・リスト"と呼ばれます。

        ステップ1C: ループ戻り値の型を決定します。

        1. 省略されたfini関数を無視して、fini関数の戻り値の型を調べます。
        2. fini関数がない場合、ループの戻り値の型はvoidです。
        3. それ以外の場合は、fini関数(戻り値の型は同じでなければならない)の共通戻り型Rがループ戻り型を定義します。

        ステップ1D: その他の型を確認してください。

        1. 少なくとも1つの省略されていないpred関数が存在する必要があります。
        2. 省略されていないすべてのpred関数には、boolean戻り型が必要です。

        ステップ2: パラメータ・リストを決定します。

        1. 結果のループ・ハンドルのパラメータ・リストは、外部パラメータ・リスト(A...)になります。
        2. init関数のパラメータ・リストは、外部パラメータ・リストに合わせて調整されます。 (パラメータ・リストは既にこのリストと実質的に同じであることに注意してください。)
        3. 初期化されていない非初期化(ステップ、プレ、フィニ)関数のパラメータ・リストは、内部パラメータ・リスト(V... A...)と実質的に同一でなければなりません。

        ステップ3: 省略された関数を入力します。

        1. init関数が省略されている場合は、句の反復変数型に「デフォルト値」を使用します。
        2. ステップ関数を省略した場合は、節の反復変数型の「アイデンティティ関数」を使用します。先行する句の非void反復変数のアイデンティティ関数パラメータの前に、削除された引数パラメータを挿入します。 (これにより、ループ変数がローカル・ループ不変になります。)
        3. pred関数を省略した場合は、定数true関数を使用してください。 (これは、この句が関係する限り、ループを続けるでしょう。 そのような場合、対応するfini関数に到達できないことに注意してください。)
        4. fini関数が省略されている場合は、ループ戻り型として「デフォルト値」を使用します。

        ステップ4: 欠落しているパラメータのタイプを入力します。

        1. この時点で、すべてのinit関数のパラメータ・リストは、外部パラメータ・リスト(A...)と実質的に同じですが、いくつかのリストは短くてもかまいません。 短いパラメータ・リストを持つすべてのinit関数に対して、リストの最後を埋めます。
        2. この時点で、非init関数の各パラメータ・リストは、内部パラメータ・リスト(V... A...)と実質的に同じですが、リストの中にはより短いものがあります。 短いパラメータ・リストを持つすべての非init関数に対して、リストの末尾を埋めます。
        3. 引数リストは「未使用の末尾の引数を削除」によって埋められます。

        最終的な観察。

        1. これらのステップの後、省略された関数と引数を指定することによって、すべての句が調整されました。
        2. すべてのinit関数は、共通のパラメータ型リスト(A...)を持っています。これは最終ループ・ハンドルにもあります。
        3. すべてのfini関数には共通の戻り型Rがあり、最終ループ・ハンドルにもこの型があります。
        4. すべての非初期化関数は、(non-void)反復変数Vとループ・パラメータのあとに共通のパラメータ型リスト(V... A...)を持ちます。
        5. init関数とstep関数の各ペアは、戻り値の型Vで一致します。
        6. 各非初期化関数は、すべての反復変数の現在の値(v...)を観測することができます。
        7. すべての関数は、すべてのループ・パラメータの入力値(a...)を観測できます。

        上記のステップ1Aの結果、loopコンビネータは次のプロパティを持ちます:

        • Nn = 1..NCn = {null, Sn, Pn}を指定すると仮定します。
        • 述語ハンドルPnnullであるか、パラメータがないとします。 (1つのPnは、非nullでなければなりません。)
        • ステップ・ハンドルSnが、いくつかの定数X>=Nに対して(B1..BX)Rnのシグネチャを持つとします。
        • Qが非空白型のRnの数であり、(V1...VQ)がそれらの型のシーケンスであるとします。
        • n = 1..min(X,Q)Vn == Bnでなければなりません。
        • パラメータ型Vnは、ループ・ローカル状態エレメント(V...)として解釈されます。
        • 残りのすべての型BQ+1..BX (Q<Xの場合)は、結果のループ・ハンドル・パラメータ型(A...)を決定します。
        この例では、ループ・ハンドル・パラメータ(A...)はステップ関数から導出されました。これは、ループ計算の大部分がステップで発生した場合に当然です。 いくつかのループでは、計算の負担がpred関数で最も重くなる可能性があるため、pred関数はループ・パラメータ値を受け入れる必要があります。 複雑な出口ロジックを持つループの場合、fini関数はループ・パラメータを受け入れる必要があり、同様に複雑な入口ロジックを持つループの場合、init関数には余分なパラメータが必要です。 このような理由から、これらのパラメータを決定するためのルールは、すべての句部分にわたって可能な限り対称です。 一般に、ループ・パラメータはループ全体にわたって共通の不変値として機能し、反復変数は一般的なバリアント値として機能し、内部ループ不変の一時変数として(ステップ機能がない場合)として機能します。

        ループ実行。

        1. ループが呼び出されると、ループ入力値はすべての句機能に渡されるようにローカルに保存されます。 これらのローカルはループ不変です。
        2. 各init関数は、(外部引数(a...)を渡します。)の句の順番で実行され、非voidの値は(反復変数として(v...))ローカルに保存されます。 これらのローカルの人々は、ループ変化(上記のように、ステップが同一性機能として動作しない限り)になります。
        3. すべての関数実行(init関数を除く)には、(v...) (in句の順)以外のvoid反復値(v...)と内部ループ入力(a...) (引数順に)からなる内部パラメータ・リストが渡されます。
        4. 次に、pred関数がfalseを返すまで、stepおよびpred関数が句の順(前の一歩)で実行されます。
        5. ステップ関数呼び出しからの非void結果は、ループ変数のシーケンス(v...)の対応する値を更新するために使用されます。 更新された値は、後続のすべての関数呼び出しでただちに表示されます。
        6. pred関数がfalseを返した場合は、対応するfini関数が呼び出され、結果の値(型Rの)がループ全体から返されます。
        7. すべてのpred関数が常にtrueを返す場合、fini関数は呼び出されず、例外をスローする以外はループを終了できません。

        使用上のヒント

        • 各ステップ関数はループ変数のallの現在の値を受け取りますが、時にはステップ関数はそれ自身の変数の現在の値を観測するだけです。 その場合、ステップ関数は明示的に「前のすべてのループ変数を削除」を必要とするかもしれません。 これには、型をdropArguments(step, 0, V0.class, ...)のような式で記述する必要があります。
        • ループ変数は変更する必要はありません。ループ不変である可能性があります。 句は、step、pred、またはfini関数を持たない適切なinit関数によってループ不変式を作成することができます。 これは、着信ループ引数を隣接するループ変数のステップまたはpred関数に"wire"するのに便利です。
        • 句関数のいくつかがインスタンス上の仮想メソッドである場合、インスタンス自体は、new MethodHandle[]{identity(ObjType.class)}のような最初の句を使用して、初期不変ループ"variable"に配置すると便利です。 その場合、インスタンス参照は最初の反復変数の値になり、仮想メソッドを句部分として使用するのは簡単です。そのすべてがその値に一致する先行するインスタンス参照を取るからです。

        結果のループ・ハンドルの疑似コードを次に示します。 上記のように、Vvはループ変数の型と値を表します。Aaは、ループ全体に渡される引数を表します。Rは、すべてのファイナライザと結果のループの共通の結果型です。

        
         V... init...(A...);
         boolean pred...(V..., A...);
         V... step...(V..., A...);
         R fini...(V..., A...);
         R loop(A... a) {
           V... v... = init...(a...);
           for (;;) {
             for ((v, p, s, f) in (v..., pred..., step..., fini...)) {
               v = s(v..., a...);
               if (!p(v..., a...)) {
                 return f(v..., a...);
               }
             }
           }
         }
         
        個々の句機能がすべてを怠るかもしれないとしても、パラメータ型リスト(V...)および(A...)は完全長に拡張されていることに注意してください。 上記のように、欠落しているパラメータは、dropArgumentsToMatch(java.lang.invoke.MethodHandle, int, java.util.List<java.lang.Class<?>>, int, boolean)のように入力されます。

        APIの注:
        例:
        
         // iterative implementation of the factorial function as a loop handle
         static int one(int k) { return 1; }
         static int inc(int i, int acc, int k) { return i + 1; }
         static int mult(int i, int acc, int k) { return i * acc; }
         static boolean pred(int i, int acc, int k) { return i < k; }
         static int fin(int i, int acc, int k) { return acc; }
         // assume MH_one, MH_inc, MH_mult, MH_pred, and MH_fin are handles to the above methods
         // null initializer for counter, should initialize to 0
         MethodHandle[] counterClause = new MethodHandle[]{null, MH_inc};
         MethodHandle[] accumulatorClause = new MethodHandle[]{MH_one, MH_mult, MH_pred, MH_fin};
         MethodHandle loop = MethodHandles.loop(counterClause, accumulatorClause);
         assertEquals(120, loop.invoke(5));
         
        同じ例、引数の削除と結合子の使用:
        
         // simplified implementation of the factorial function as a loop handle
         static int inc(int i) { return i + 1; } // drop acc, k
         static int mult(int i, int acc) { return i * acc; } //drop k
         static boolean cmp(int i, int k) { return i < k; }
         // assume MH_inc, MH_mult, and MH_cmp are handles to the above methods
         // null initializer for counter, should initialize to 0
         MethodHandle MH_one = MethodHandles.constant(int.class, 1);
         MethodHandle MH_pred = MethodHandles.dropArguments(MH_cmp, 1, int.class); // drop acc
         MethodHandle MH_fin = MethodHandles.dropArguments(MethodHandles.identity(int.class), 0, int.class); // drop i
         MethodHandle[] counterClause = new MethodHandle[]{null, MH_inc};
         MethodHandle[] accumulatorClause = new MethodHandle[]{MH_one, MH_mult, MH_pred, MH_fin};
         MethodHandle loop = MethodHandles.loop(counterClause, accumulatorClause);
         assertEquals(720, loop.invoke(6));
         
        ヘルパー・オブジェクトを使用してループ・パラメータを保持する同様の例:
        
         // instance-based implementation of the factorial function as a loop handle
         static class FacLoop {
           final int k;
           FacLoop(int k) { this.k = k; }
           int inc(int i) { return i + 1; }
           int mult(int i, int acc) { return i * acc; }
           boolean pred(int i) { return i < k; }
           int fin(int i, int acc) { return acc; }
         }
         // assume MH_FacLoop is a handle to the constructor
         // assume MH_inc, MH_mult, MH_pred, and MH_fin are handles to the above methods
         // null initializer for counter, should initialize to 0
         MethodHandle MH_one = MethodHandles.constant(int.class, 1);
         MethodHandle[] instanceClause = new MethodHandle[]{MH_FacLoop};
         MethodHandle[] counterClause = new MethodHandle[]{null, MH_inc};
         MethodHandle[] accumulatorClause = new MethodHandle[]{MH_one, MH_mult, MH_pred, MH_fin};
         MethodHandle loop = MethodHandles.loop(instanceClause, counterClause, accumulatorClause);
         assertEquals(5040, loop.invoke(7));
         
        パラメータ:
        clauses - 上記の規則に従ったMethodHandleの(4-tuples)の配列の配列。
        戻り値:
        引数によって定義されたループ動作を具現化するメソッド・ハンドル。
        例外:
        IllegalArgumentException - 上記の制約のいずれかに違反した場合。
        導入されたバージョン:
        9
        関連項目:
        whileLoop(MethodHandle, MethodHandle, MethodHandle), doWhileLoop(MethodHandle, MethodHandle, MethodHandle), countedLoop(MethodHandle, MethodHandle, MethodHandle), iteratedLoop(MethodHandle, MethodHandle, MethodHandle)
      • whileLoop

        public static MethodHandle whileLoop​(MethodHandle init,
                                             MethodHandle pred,
                                             MethodHandle body)
        初期化子、本文、述語からwhileループを構築します。 これは「ジェネリック・ループ・コンビネータ」の便利なラッパーです。

        predハンドルはループ条件を記述します。body、その本文。 このメソッドで得られたループは、各反復で、最初に述語を評価し、その本文(述語がtrueと評価された場合)を実行します。 述部がfalse (この場合、本文は実行されません)に評価されると、ループは終了します。

        initハンドルは、追加のオプションのループ・ローカル変数の初期値を記述します。 各反復で、このループ・ローカル変数が存在する場合、変数はbodyに渡され、その呼び出しから返された値で更新されます。 ループ実行の結果は、追加のループ・ローカル変数(存在する場合)の最終値になります。

        これらの引数ハンドルには、次の規則があります:

        • bodyハンドルはnullであってはなりません。その型は、(V A...)Vの形式でなければなりません。ここで、Vは非voidです。そうでなければ(A...)voidです。 (voidの場合、型voidVという名前に割り当て、voidVがパラメータ・リストから静かに削除され、(A...)Vを残していることを理解して(V A...)Vを記述します。)
        • 本文のパラメータ・リスト(V A...)は、「内部パラメータ・リスト」と呼ばれます。 他のループ部分のパラメータ・リストを制約します。
        • 反復変数型Vが内部パラメータ・リストから削除された場合、結果として得られる短いリスト(A...)「外部パラメータ・リスト」と呼ばれます。
        • 本文戻り型V(非voidの場合)は、ループの追加の状態変数の型を決定します。 本文は、この型の値を受け入れて返す必要があります。V
        • initが非nullの場合、戻り型はVでなければなりません。 そのパラメータ・リスト(いくつかの「フォーム(A*))は、外部パラメータ・リスト(A...)に対して「効果的に同一」でなければなりません。
        • initnullの場合、ループ変数は「デフォルト値」に初期化されます。
        • predハンドルは、nullであってはなりません。 戻り値の型としてbooleanが必要です。 そのパラメータ・リスト(空または(V A*)の形式のいずれか)は、内部パラメータ・リストと実質的に同一でなければなりません。

        結果のループ・ハンドルの結果の型とパラメータのシグネチャは、次のように決定されます:

        • ループ・ハンドルの結果型は、本文の結果型Vです。
        • ループ・ハンドル・パラメータ型は、外部パラメータ・リストから、型(A...)です。

        結果のループ・ハンドルの疑似コードを次に示します。 コードでは、V / vは、単独のループ変数の型/値とループの結果型を表します。A / a、ループに渡された引数の値。

        
         V init(A...);
         boolean pred(V, A...);
         V body(V, A...);
         V whileLoop(A... a...) {
           V v = init(a...);
           while (pred(v, a...)) {
             v = body(v, a...);
           }
           return v;
         }
         

        APIの注:
        例:
        
         // implement the zip function for lists as a loop handle
         static List<String> initZip(Iterator<String> a, Iterator<String> b) { return new ArrayList<>(); }
         static boolean zipPred(List<String> zip, Iterator<String> a, Iterator<String> b) { return a.hasNext() && b.hasNext(); }
         static List<String> zipStep(List<String> zip, Iterator<String> a, Iterator<String> b) {
           zip.add(a.next());
           zip.add(b.next());
           return zip;
         }
         // assume MH_initZip, MH_zipPred, and MH_zipStep are handles to the above methods
         MethodHandle loop = MethodHandles.whileLoop(MH_initZip, MH_zipPred, MH_zipStep);
         List<String> a = Arrays.asList("a", "b", "c", "d");
         List<String> b = Arrays.asList("e", "f", "g", "h");
         List<String> zipped = Arrays.asList("a", "e", "b", "f", "c", "g", "d", "h");
         assertEquals(zipped, (List<String>) loop.invoke(a.iterator(), b.iterator()));
         

        このメソッドの実装は、次のように表現できます:

        
         MethodHandle whileLoop(MethodHandle init, MethodHandle pred, MethodHandle body) {
             MethodHandle fini = (body.type().returnType() == void.class
                                 ? null : identity(body.type().returnType()));
             MethodHandle[]
                 checkExit = { null, null, pred, fini },
                 varBody   = { init, body };
             return loop(checkExit, varBody);
         }
         
        パラメータ:
        init - ループ変数の初期値を提供するオプションの初期化子。 デフォルトの初期値を意味するnullかもしれません。 他の制約については上記を参照してください。
        pred - ループの条件はnullではない可能性があります。 結果の型はbooleanでなければなりません。 他の制約については上記を参照してください。
        body - ループの本文ですが、nullではありません。 ループ・パラメータと結果型を制御します。 他の制約については上記を参照してください。
        戻り値:
        引数で説明されているようにwhileループを実装するメソッド・ハンドル。
        例外:
        IllegalArgumentException - 引数の規則に違反している場合。
        NullPointerException - predまたはbodynullの場合。
        導入されたバージョン:
        9
        関連項目:
        loop(MethodHandle[][]), doWhileLoop(MethodHandle, MethodHandle, MethodHandle)
      • doWhileLoop

        public static MethodHandle doWhileLoop​(MethodHandle init,
                                               MethodHandle body,
                                               MethodHandle pred)
        初期化子、本文、述語からdo-whileループを構築します。 これは「ジェネリック・ループ・コンビネータ」の便利なラッパーです。

        predハンドルはループ条件を記述します。body、その本文。 このメソッドで得られたループは、各反復で最初にその本文を実行し、次に述語を評価します。 本文の実行後、述部がfalseと評価されると、ループは終了します。

        initハンドルは、追加のオプションのループ・ローカル変数の初期値を記述します。 各反復で、このループ・ローカル変数が存在する場合、変数はbodyに渡され、その呼び出しから返された値で更新されます。 ループ実行の結果は、追加のループ・ローカル変数(存在する場合)の最終値になります。

        これらの引数ハンドルには、次の規則があります:

        • bodyハンドルはnullであってはなりません。その型は、(V A...)Vの形式でなければなりません。ここで、Vは非voidです。そうでなければ(A...)voidです。 (voidの場合、型voidVという名前に割り当て、voidVがパラメータ・リストから静かに削除され、(A...)Vを残していることを理解して(V A...)Vを記述します。)
        • 本文のパラメータ・リスト(V A...)は、「内部パラメータ・リスト」と呼ばれます。 他のループ部分のパラメータ・リストを制約します。
        • 反復変数型Vが内部パラメータ・リストから削除された場合、結果として得られる短いリスト(A...)「外部パラメータ・リスト」と呼ばれます。
        • 本文戻り型V(非voidの場合)は、ループの追加の状態変数の型を決定します。 本文は、この型の値を受け入れて返す必要があります。V
        • initが非nullの場合、戻り型はVでなければなりません。 そのパラメータ・リスト(いくつかの「フォーム(A*))は、外部パラメータ・リスト(A...)に対して「効果的に同一」でなければなりません。
        • initnullの場合、ループ変数は「デフォルト値」に初期化されます。
        • predハンドルは、nullであってはなりません。 戻り値の型としてbooleanが必要です。 そのパラメータ・リスト(空または(V A*)の形式のいずれか)は、内部パラメータ・リストと実質的に同一でなければなりません。

        結果のループ・ハンドルの結果の型とパラメータのシグネチャは、次のように決定されます:

        • ループ・ハンドルの結果型は、本文の結果型Vです。
        • ループ・ハンドル・パラメータ型は、外部パラメータ・リストから、型(A...)です。

        結果のループ・ハンドルの疑似コードを次に示します。 コードでは、V / vは、単独のループ変数の型/値とループの結果型を表します。A / a、ループに渡された引数の値。

        
         V init(A...);
         boolean pred(V, A...);
         V body(V, A...);
         V doWhileLoop(A... a...) {
           V v = init(a...);
           do {
             v = body(v, a...);
           } while (pred(v, a...));
           return v;
         }
         

        APIの注:
        例:
        
         // int i = 0; while (i < limit) { ++i; } return i; => limit
         static int zero(int limit) { return 0; }
         static int step(int i, int limit) { return i + 1; }
         static boolean pred(int i, int limit) { return i < limit; }
         // assume MH_zero, MH_step, and MH_pred are handles to the above methods
         MethodHandle loop = MethodHandles.doWhileLoop(MH_zero, MH_step, MH_pred);
         assertEquals(23, loop.invoke(23));
         

        このメソッドの実装は、次のように表現できます:

        
         MethodHandle doWhileLoop(MethodHandle init, MethodHandle body, MethodHandle pred) {
             MethodHandle fini = (body.type().returnType() == void.class
                                 ? null : identity(body.type().returnType()));
             MethodHandle[] clause = { init, body, pred, fini };
             return loop(clause);
         }
         
        パラメータ:
        init - ループ変数の初期値を提供するオプションの初期化子。 デフォルトの初期値を意味するnullかもしれません。 他の制約については上記を参照してください。
        body - ループの本文ですが、nullではありません。 ループ・パラメータと結果型を制御します。 他の制約については上記を参照してください。
        pred - ループの条件はnullではない可能性があります。 結果の型はbooleanでなければなりません。 他の制約については上記を参照してください。
        戻り値:
        引数で説明されているようにwhileループを実装するメソッド・ハンドル。
        例外:
        IllegalArgumentException - 引数の規則に違反している場合。
        NullPointerException - predまたはbodynullの場合。
        導入されたバージョン:
        9
        関連項目:
        loop(MethodHandle[][]), whileLoop(MethodHandle, MethodHandle, MethodHandle)
      • countedLoop

        public static MethodHandle countedLoop​(MethodHandle iterations,
                                               MethodHandle init,
                                               MethodHandle body)
        指定された回数の反復を実行するループを構築します。 これは「ジェネリック・ループ・コンビネータ」の便利なラッパーです。

        反復回数は、iterationsハンドル評価結果によって決まります。 ループ・カウンタiは、型intの余分なループ反復変数です。 0に初期化され、各反復で1ずつインクリメントされます。

        bodyハンドルが非voidVを戻す場合、その型の先頭ループ反復変数も存在します。 この変数は、オプションのinitハンドルを使用して、または型V「デフォルト値」(ハンドルがnullの場合)を使用して初期化されます。

        各反復では、反復変数がbodyハンドルの呼び出しに渡されます。 本文(型Vの)から返された非void値は、先頭の反復変数を更新します。 ループ・ハンドル実行の結果は、その変数(V変数がない場合はvoid)の最後のV値になります。

        引き数ハンドルには次の規則があります:

        • iterationsハンドルはnullであってはならず、パラメータ型リストでここではIと呼ばれる型intを返す必要があります。
        • bodyハンドルはnullであってはなりません。その型は、(V I A...)Vの形式でなければなりません。ここで、Vは非voidです。そうでなければ(I A...)voidです。 (voidの場合、型voidVという名前に割り当て、voidVがパラメータ・リストから静かに削除され、(I A...)Vを残していることを理解して(V I A...)Vを記述します。)
        • 本文のパラメータ・リスト(V I A...)は、「内部パラメータ・リスト」と呼ばれる型のリストに寄与します。 他のループ部分のパラメータ・リストを制約します。
        • 特殊なケースとして、本文がVI型のみを追加し、A型を追加しない場合、内部パラメータ・リストはiterationsハンドルの引数型A...によって拡張されます。
        • 反復変数型(V I)が内部パラメータ・リストから削除された場合、結果として得られる短いリスト(A...)「外部パラメータ・リスト」と呼ばれます。
        • 本文戻り型V(非voidの場合)は、ループの追加の状態変数の型を決定します。 本文は先行するパラメータを受け入れ、この型の値Vを戻す必要があります。
        • initが非nullの場合、戻り型はVでなければなりません。 そのパラメータ・リスト(いくつかの「フォーム(A*))は、外部パラメータ・リスト(A...)に対して「効果的に同一」でなければなりません。
        • initnullの場合、ループ変数は「デフォルト値」に初期化されます。
        • iterations (いくつかの形式の(A*))のパラメータ・リストは、外部パラメータ・リスト(A...)と実質的に同一でなければなりません。

        結果のループ・ハンドルの結果の型とパラメータのシグネチャは、次のように決定されます:

        • ループ・ハンドルの結果型は、本文の結果型Vです。
        • ループ・ハンドル・パラメータ型は、外部パラメータ・リストから、型(A...)です。

        結果のループ・ハンドルの疑似コードを次に示します。 コードでは、V / vは、2番目のループ変数の型/値とループの結果型を表します。A... / a...は、ループに渡される引数を表します。

        
         int iterations(A...);
         V init(A...);
         V body(V, int, A...);
         V countedLoop(A... a...) {
           int end = iterations(a...);
           V v = init(a...);
           for (int i = 0; i < end; ++i) {
             v = body(v, i, a...);
           }
           return v;
         }
         

        APIの注:
        完全に適合する本文メソッドの例:
        
         // String s = "Lambdaman!"; for (int i = 0; i < 13; ++i) { s = "na " + s; } return s;
         // => a variation on a well known theme
         static String step(String v, int counter, String init) { return "na " + v; }
         // assume MH_step is a handle to the method above
         MethodHandle fit13 = MethodHandles.constant(int.class, 13);
         MethodHandle start = MethodHandles.identity(String.class);
         MethodHandle loop = MethodHandles.countedLoop(fit13, start, MH_step);
         assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke("Lambdaman!"));
         

        最も単純なbodyメソッドの型を持つ例、および繰り返し回数をループ呼び出しに渡す例:

        
         // String s = "Lambdaman!"; for (int i = 0; i < 13; ++i) { s = "na " + s; } return s;
         // => a variation on a well known theme
         static String step(String v, int counter ) { return "na " + v; }
         // assume MH_step is a handle to the method above
         MethodHandle count = MethodHandles.dropArguments(MethodHandles.identity(int.class), 1, String.class);
         MethodHandle start = MethodHandles.dropArguments(MethodHandles.identity(String.class), 0, int.class);
         MethodHandle loop = MethodHandles.countedLoop(count, start, MH_step);  // (v, i) -> "na " + v
         assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke(13, "Lambdaman!"));
         

        反復回数、追加する文字列、ループ・パラメータとして追加する文字列を処理する例:

        
         // String s = "Lambdaman!", t = "na"; for (int i = 0; i < 13; ++i) { s = t + " " + s; } return s;
         // => a variation on a well known theme
         static String step(String v, int counter, int iterations_, String pre, String start_) { return pre + " " + v; }
         // assume MH_step is a handle to the method above
         MethodHandle count = MethodHandles.identity(int.class);
         MethodHandle start = MethodHandles.dropArguments(MethodHandles.identity(String.class), 0, int.class, String.class);
         MethodHandle loop = MethodHandles.countedLoop(count, start, MH_step);  // (v, i, _, pre, _) -> pre + " " + v
         assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke(13, "na", "Lambdaman!"));
         

        ループ型を強制するためのdropArgumentsToMatch(MethodHandle, int, List, int)の使用例を示す例:

        
         // String s = "Lambdaman!", t = "na"; for (int i = 0; i < 13; ++i) { s = t + " " + s; } return s;
         // => a variation on a well known theme
         static String step(String v, int counter, String pre) { return pre + " " + v; }
         // assume MH_step is a handle to the method above
         MethodType loopType = methodType(String.class, String.class, int.class, String.class);
         MethodHandle count = MethodHandles.dropArgumentsToMatch(MethodHandles.identity(int.class),    0, loopType.parameterList(), 1);
         MethodHandle start = MethodHandles.dropArgumentsToMatch(MethodHandles.identity(String.class), 0, loopType.parameterList(), 2);
         MethodHandle body  = MethodHandles.dropArgumentsToMatch(MH_step,                              2, loopType.parameterList(), 0);
         MethodHandle loop = MethodHandles.countedLoop(count, start, body);  // (v, i, pre, _, _) -> pre + " " + v
         assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke("na", 13, "Lambdaman!"));
         

        このメソッドの実装は、次のように表現できます:

        
         MethodHandle countedLoop(MethodHandle iterations, MethodHandle init, MethodHandle body) {
             return countedLoop(empty(iterations.type()), iterations, init, body);
         }
         
        パラメータ:
        iterations - このループが実行する反復回数を返す非nullハンドル。 ハンドル結果の型はintでなければなりません。 他の制約については上記を参照してください。
        init - ループ変数の初期値を提供するオプションの初期化子。 デフォルトの初期値を意味するnullかもしれません。 他の制約については上記を参照してください。
        body - ループの本文ですが、nullではありません。 標準ケース(詳細は上記を参照)でループ・パラメータと結果型を制御します。 それは、それ自身のリターン型(非空白の場合)とintパラメータ(カウンタのために)を受け入れなければならず、任意の数の追加型を受け入れることができます。 他の制約については上記を参照してください。
        戻り値:
        ループを表すメソッド・ハンドル。
        例外:
        NullPointerException - iterationsまたはbodyハンドルのいずれかがnullの場合。
        IllegalArgumentException - 上記で定式化された規則に違反する場合。
        導入されたバージョン:
        9
        関連項目:
        countedLoop(MethodHandle, MethodHandle, MethodHandle, MethodHandle)
      • countedLoop

        public static MethodHandle countedLoop​(MethodHandle start,
                                               MethodHandle end,
                                               MethodHandle init,
                                               MethodHandle body)
        一連の数値をカウントするループを構築します。 これは「ジェネリック・ループ・コンビネータ」の便利なラッパーです。

        ループ・カウンタiは、型intのループ反復変数である。 startおよびendハンドルは、ループ・カウンタの開始(inclusive)および終了(exclusive)値を決定します。 ループ・カウンタは、startハンドルの評価から返されたint値に初期化され、ステップ幅1でend (exclusively)から返された値に実行されます。

        bodyハンドルが非voidVを戻す場合、その型の先頭ループ反復変数も存在します。 この変数は、オプションのinitハンドルを使用して、または型V「デフォルト値」(ハンドルがnullの場合)を使用して初期化されます。

        各反復では、反復変数がbodyハンドルの呼び出しに渡されます。 本文(型Vの)から返された非void値は、先頭の反復変数を更新します。 ループ・ハンドル実行の結果は、その変数(V変数がない場合はvoid)の最後のV値になります。

        引き数ハンドルには次の規則があります:

        • startおよびendハンドルはnullであってはいけません。両方とも、共通型int(I)をパラメータ型リストで戻す必要があります。
        • bodyハンドルはnullであってはなりません。その型は、(V I A...)Vの形式でなければなりません。ここで、Vは非voidです。そうでなければ(I A...)voidです。 (voidの場合、型voidVという名前に割り当て、voidVがパラメータ・リストから静かに削除され、(I A...)Vを残していることを理解して(V I A...)Vを記述します。)
        • 本文のパラメータ・リスト(V I A...)は、「内部パラメータ・リスト」と呼ばれる型のリストに寄与します。 他のループ部分のパラメータ・リストを制約します。
        • 特殊なケースとして、本文がVI型のみを追加し、A型を追加しない場合、内部パラメータ・リストはendハンドルの引数型A...によって拡張されます。
        • 反復変数型(V I)が内部パラメータ・リストから削除された場合、結果として得られる短いリスト(A...)「外部パラメータ・リスト」と呼ばれます。
        • 本文戻り型V(非voidの場合)は、ループの追加の状態変数の型を決定します。 本文は先行するパラメータを受け入れ、この型の値Vを戻す必要があります。
        • initが非nullの場合、戻り型はVでなければなりません。 そのパラメータ・リスト(いくつかの「フォーム(A*))は、外部パラメータ・リスト(A...)に対して「効果的に同一」でなければなりません。
        • initnullの場合、ループ変数は「デフォルト値」に初期化されます。
        • start (いくつかの形式の(A*))のパラメータ・リストは、外部パラメータ・リスト(A...)と実質的に同一でなければなりません。
        • 同様に、endのパラメータ・リストは、外部パラメータ・リストと実質的に同一でなければなりません。

        結果のループ・ハンドルの結果の型とパラメータのシグネチャは、次のように決定されます:

        • ループ・ハンドルの結果型は、本文の結果型Vです。
        • ループ・ハンドル・パラメータ型は、外部パラメータ・リストから、型(A...)です。

        結果のループ・ハンドルの疑似コードを次に示します。 コードでは、V / vは、2番目のループ変数の型/値とループの結果型を表します。A... / a...は、ループに渡される引数を表します。

        
         int start(A...);
         int end(A...);
         V init(A...);
         V body(V, int, A...);
         V countedLoop(A... a...) {
           int e = end(a...);
           int s = start(a...);
           V v = init(a...);
           for (int i = s; i < e; ++i) {
             v = body(v, i, a...);
           }
           return v;
         }
         

        APIの注:
        このメソッドの実装は、以下のように表すことができる:
        
         MethodHandle countedLoop(MethodHandle start, MethodHandle end, MethodHandle init, MethodHandle body) {
             MethodHandle returnVar = dropArguments(identity(init.type().returnType()), 0, int.class, int.class);
             // assume MH_increment and MH_predicate are handles to implementation-internal methods with
             // the following semantics:
             // MH_increment: (int limit, int counter) -> counter + 1
             // MH_predicate: (int limit, int counter) -> counter < limit
             Class<?> counterType = start.type().returnType();  // int
             Class<?> returnType = body.type().returnType();
             MethodHandle incr = MH_increment, pred = MH_predicate, retv = null;
             if (returnType != void.class) {  // ignore the V variable
                 incr = dropArguments(incr, 1, returnType);  // (limit, v, i) => (limit, i)
                 pred = dropArguments(pred, 1, returnType);  // ditto
                 retv = dropArguments(identity(returnType), 0, counterType); // ignore limit
             }
             body = dropArguments(body, 0, counterType);  // ignore the limit variable
             MethodHandle[]
                 loopLimit  = { end, null, pred, retv }, // limit = end(); i < limit || return v
                 bodyClause = { init, body },            // v = init(); v = body(v, i)
                 indexVar   = { start, incr };           // i = start(); i = i + 1
             return loop(loopLimit, bodyClause, indexVar);
         }
         
        パラメータ:
        start -intでなければならないループ・カウンタの開始値を返すための非nullハンドル。 他の制約については上記を参照してください。
        end - ループ・カウンタ(ループはend-1に実行されます)の終了値を返す非nullハンドル。 結果の型はintでなければなりません。 他の制約については上記を参照してください。
        init - ループ変数の初期値を提供するオプションの初期化子。 デフォルトの初期値を意味するnullかもしれません。 他の制約については上記を参照してください。
        body - ループの本文ですが、nullではありません。 標準ケース(詳細は上記を参照)でループ・パラメータと結果型を制御します。 それは、それ自身のリターン型(非空白の場合)とintパラメータ(カウンタのために)を受け入れなければならず、任意の数の追加型を受け入れることができます。 他の制約については上記を参照してください。
        戻り値:
        ループを表すメソッド・ハンドル。
        例外:
        NullPointerException - startend、またはbodyハンドルのいずれかがnullである場合。
        IllegalArgumentException - 上記で定式化された規則に違反する場合。
        導入されたバージョン:
        9
        関連項目:
        countedLoop(MethodHandle, MethodHandle, MethodHandle)
      • iteratedLoop

        public static MethodHandle iteratedLoop​(MethodHandle iterator,
                                                MethodHandle init,
                                                MethodHandle body)
        Iterator<T>によって生成された値の範囲にあるループを構築します。 これは「ジェネリック・ループ・コンビネータ」の便利なラッパーです。

        イテレータ自体は、iteratorハンドルの評価によって決定されます。 生成される各値は、型Tのループ反復変数に格納されます。

        bodyハンドルが非voidVを戻す場合、その型の先頭ループ反復変数も存在します。 この変数は、オプションのinitハンドルを使用して、または型V「デフォルト値」(ハンドルがnullの場合)を使用して初期化されます。

        各反復では、反復変数がbodyハンドルの呼び出しに渡されます。 本文(型Vの)から返された非void値は、先頭の反復変数を更新します。 ループ・ハンドル実行の結果は、その変数(V変数がない場合はvoid)の最後のV値になります。

        引き数ハンドルには次の規則があります:

        • bodyハンドルはnullであってはなりません。その型は、(V T A...)Vの形式でなければなりません。ここで、Vは非voidです。そうでなければ(T A...)voidです。 (voidの場合、型voidVという名前に割り当て、voidVがパラメータ・リストから静かに削除され、(T A...)Vを残していることを理解して(V T A...)Vを記述します。)
        • 本文のパラメータ・リスト(V T A...)は、「内部パラメータ・リスト」と呼ばれる型のリストに寄与します。 他のループ部分のパラメータ・リストを制約します。
        • 特殊なケースとして、本文がVTの型だけに貢献し、追加のA型を持たない場合、内部パラメータ・リストはiteratorハンドルの引数型A...によって拡張されます。nullの場合、単一型のIterableが追加され、A...リストを構成します。
        • 反復変数型(V T)が内部パラメータ・リストから削除された場合、結果として得られる短いリスト(A...)「外部パラメータ・リスト」と呼ばれます。
        • 本文戻り型V(非voidの場合)は、ループの追加の状態変数の型を決定します。 本文は先行するパラメータを受け入れ、この型の値Vを戻す必要があります。
        • initが非nullの場合、戻り型はVでなければなりません。 そのパラメータ・リスト(いくつかの「フォーム(A*))は、外部パラメータ・リスト(A...)に対して「効果的に同一」でなければなりません。
        • initnullの場合、ループ変数は「デフォルト値」に初期化されます。
        • iteratorハンドルがnull以外の場合は、戻り型java.util.Iteratorまたはそのサブ型を持たなければなりません。 ループが実行されたときに生成されるイテレータは、T型に変換可能な値を生成すると見なされます。
        • null (いくつかの形式の(A*))であるiteratorのパラメータ・リストは、外部パラメータ・リスト(A...)と実質的に同一でなければなりません。
        • iteratornullの場合、デフォルトではIterable.iterator()のように動作するメソッド・ハンドルになります。 この場合、内部パラメータ・リスト(V T A...)には少なくとも1つのA型が必要です。また、デフォルト・イテレータ・ハンドル・パラメータは、asType変換メソッドの場合と同様に、先頭のA型を受け入れるように調整されます。 先頭のA型は、Iterableまたはそのサブ型でなければなりません。 この変換ステップは、ループ構築時に実行され、WrongMethodTypeExceptionを送出してはなりません。

        Tは、プリミティブでも参照でもよい。 型ハンドルIterator<T>は、メソッド・ハンドル表現で生の型Iteratorに消去されるので、iteratedLoopコンビネータは、bodyの先頭引数型を、asType変換メソッドの場合と同様にObjectに調整します。 したがって、ループが実行されるときに間違った型のイテレータが表示された場合、MethodHandle.asType(MethodType)によって実行される動的変換の結果として実行時例外が発生することがあります。

        結果のループ・ハンドルの結果の型とパラメータのシグネチャは、次のように決定されます:

        • ループ・ハンドルの結果型は、本文の結果型Vです。
        • ループ・ハンドル・パラメータ型は、外部パラメータ・リストから、型(A...)です。

        結果のループ・ハンドルの疑似コードを次に示します。 コードでは、V / vは、ループ変数の型/値とループの結果型を表します。T / t、ループが繰り返す構造の要素のもの、A... / a...は、ループに渡される引数を表します。

        
         Iterator<T> iterator(A...);  // defaults to Iterable::iterator
         V init(A...);
         V body(V,T,A...);
         V iteratedLoop(A... a...) {
           Iterator<T> it = iterator(a...);
           V v = init(a...);
           while (it.hasNext()) {
             T t = it.next();
             v = body(v, t, a...);
           }
           return v;
         }
         

        APIの注:
        例:
        
         // get an iterator from a list
         static List<String> reverseStep(List<String> r, String e) {
           r.add(0, e);
           return r;
         }
         static List<String> newArrayList() { return new ArrayList<>(); }
         // assume MH_reverseStep and MH_newArrayList are handles to the above methods
         MethodHandle loop = MethodHandles.iteratedLoop(null, MH_newArrayList, MH_reverseStep);
         List<String> list = Arrays.asList("a", "b", "c", "d", "e");
         List<String> reversedList = Arrays.asList("e", "d", "c", "b", "a");
         assertEquals(reversedList, (List<String>) loop.invoke(list));
         

        このメソッドの実装は、およそ以下のように表現することができる:

        
         MethodHandle iteratedLoop(MethodHandle iterator, MethodHandle init, MethodHandle body) {
             // assume MH_next, MH_hasNext, MH_startIter are handles to methods of Iterator/Iterable
             Class<?> returnType = body.type().returnType();
             Class<?> ttype = body.type().parameterType(returnType == void.class ? 0 : 1);
             MethodHandle nextVal = MH_next.asType(MH_next.type().changeReturnType(ttype));
             MethodHandle retv = null, step = body, startIter = iterator;
             if (returnType != void.class) {
                 // the simple thing first:  in (I V A...), drop the I to get V
                 retv = dropArguments(identity(returnType), 0, Iterator.class);
                 // body type signature (V T A...), internal loop types (I V A...)
                 step = swapArguments(body, 0, 1);  // swap V <-> T
             }
             if (startIter == null)  startIter = MH_getIter;
             MethodHandle[]
                 iterVar    = { startIter, null, MH_hasNext, retv }, // it = iterator; while (it.hasNext())
                 bodyClause = { init, filterArguments(step, 0, nextVal) };  // v = body(v, t, a)
             return loop(iterVar, bodyClause);
         }
         
        パラメータ:
        iterator - ループを開始するイテレータを返すためのオプションのハンドル。 nullの場合、ハンドルはIteratorまたはサブ型を戻す必要があります。 他の制約については上記を参照してください。
        init - ループ変数の初期値を提供するオプションの初期化子。 デフォルトの初期値を意味するnullかもしれません。 他の制約については上記を参照してください。
        body - ループの本文ですが、nullではありません。 標準ケース(詳細は上記を参照)でループ・パラメータと結果型を制御します。 それは、それ自身の戻り値の型(非空白の場合)とTパ・ラメタ(反復された値)を受け入れなければならず、任意の数の追加型を受け入れることができます。 他の制約については上記を参照してください。
        戻り値:
        反復ループ機能を具現化するメソッド・ハンドル。
        例外:
        NullPointerException - bodyハンドルがnullの場合。
        IllegalArgumentException - いずれかの議論が上記の要件に違反する場合。
        導入されたバージョン:
        9
      • tryFinally

        public static MethodHandle tryFinally​(MethodHandle target,
                                              MethodHandle cleanup)
        targetメソッドのハンドルをtry-finallyブロックにラップすることによって処理するメソッド・ハンドルを作成します。 他のメソッド・ハンドル、cleanupは、finallyブロックの機能を表します。 targetハンドルの実行中にスローされた例外は、cleanupハンドルに渡されます。 cleanupハンドルが例外を最初にスローしない限り、例外は再スローされます。 cleanupハンドル実行から返される値は、try-finallyハンドルの実行結果です。

        cleanupハンドルには、1つまたは2つの追加の先行引数が渡されます。 1つ目は、targetハンドルの実行中にスローされた例外、または例外がスローされなかった場合はnullです。 2番目はtargetハンドルの実行結果で、例外がスローされた場合は、必要な型のnull、zero、またはfalseの値がプレースホルダーとして提供されます。 targetハンドルにvoid戻り型がある場合、2番目の引数は存在しません。 (引数の型変換を除いて、結合子は、対応する逆説的な引数を省略し、nullまたはゼロ値を挿入するのではなく、パラメータ・リストのvoid値を表します。)

        targetハンドルとcleanupハンドルには、対応する引数と戻り値の型が同じでなければなりません。ただし、cleanupハンドルで後続の引数が省略される場合があります。 また、cleanupハンドルには、1つまたは2つの追加の先行パラメータが必要です:

        • Throwableは、targetは、targetハンドル(もしあれば)によってスローされた例外を運びます。そして
        • targetcleanupの両方の戻り型と同じ型のパラメータで、targetハンドルの実行結果を保持します。 targetvoidを返した場合、このパラメータは存在しません。

        結果のアダプタの擬似コードは、以下のようになります。 コードでは、Vtry/finally構文の結果型を表します。A / a、クリーンアップによって消費される結果ハンドルに対する引数の型と値。B / bは、クリーンアップによって破棄された結果のハンドルへの引数のものです。

        
         V target(A..., B...);
         V cleanup(Throwable, V, A...);
         V adapter(A... a, B... b) {
           V result = (zero value for V);
           Throwable throwable = null;
           try {
             result = target(a..., b...);
           } catch (Throwable t) {
             throwable = t;
             throw t;
           } finally {
             result = cleanup(throwable, result, a...);
           }
           return result;
         }
         

        保存された引数(擬似コード内のa...)は、ターゲットの実行によって変更できないので、呼び出された場合は、呼び出し側からクリーンアップにそのまま渡されます。

        クリーンアップが常にスローされたとしても、ターゲットとクリーンアップは同じ型を返す必要があります。 このようなスロー・クリーン・アップを作成するには、throwExceptionを使用してクリーンアップ・ロジックを作成し、正しい戻り値型のメソッド・ハンドルを作成します。

        tryFinallyは例外を通常のリターンに変換しないことに注意してください。 まれに、例外をそのように変換する必要がある場合は、まずcatchException(MethodHandle, Class, MethodHandle)でターゲットをラップして送信例外をキャプチャし、tryFinallyでラップします。

        パラメータ:
        target - 実行がtryブロックにラップされるハンドル。
        cleanup - finallyブロックで呼び出されたハンドル。
        戻り値:
        2つの引数からなるtry-finallyブロックを具現化するメソッド・ハンドル。
        例外:
        NullPointerException - いずれかの引数がnullの場合
        IllegalArgumentException - cleanupが必要な先行引数を受け入れない場合、またはメソッドのハンドル型が戻り値の型および対応する末尾のパラメータで一致しない場合
        導入されたバージョン:
        9
        関連項目:
        catchException(MethodHandle, Class, MethodHandle)