5

MethodHandle::invoke または MethodHandle::invokeExact を、MethodHandle を受け入れて生成された出力を返す関数インターフェイスのメソッド参照として使用できるかどうかを確認しようとしていました。

(invoke と invokeExact はシグネチャ ポリモーフィックであるため、InvokeExact でのメタファクトリ呼び出しであることはわかっています。ただし、invoke/invokeExact の適切なバージョンを派生させるために必要だった作業をコンパイラが省略できるかどうかを知りたかったのです。)

呼び出す.InvokeExact0

package invoke;

import java.lang.invoke.MethodHandle;

import static java.lang.System.out;
import static java.lang.invoke.LambdaMetafactory.metafactory;
import static java.lang.invoke.MethodHandles.lookup;
import static java.lang.invoke.MethodType.methodType;

@FunctionalInterface
public interface InvokeExact0<OUTPUT> {
  public OUTPUT invokeExact(MethodHandle methodHandle) throws Throwable;

  public static <OUTPUT> InvokeExact0<OUTPUT> new_(InvokeExact0<OUTPUT> invokeExact) {
    return invokeExact;
  }

  public static void main(String... arguments) throws Throwable {
    out.println(
      (InvokeExact0<String>) metafactory(
        lookup(),
        "invokeExact",
        methodType(InvokeExact0.class),
        methodType(
          Object.class,
          MethodHandle.class
        ),
        lookup().findVirtual(
          MethodHandle.class,
          "invokeExact",
          methodType(String.class)
        ),
        methodType(
          String.class,
          MethodHandle.class
        )
      )
        .getTarget()
        .invokeExact()
    );
    out.println(InvokeExact0.new_(MethodHandle::invokeExact));
  }
}

結果

invoke.InvokeExact0$$Lambda$1/1878246837@5ca881b5                                                                                                                         
Exception in thread "main" java.lang.BootstrapMethodError: call site initialization exception                                                                             
        at java.lang.invoke.CallSite.makeSite(CallSite.java:328)                                                                                                          
        at java.lang.invoke.MethodHandleNatives.linkCallSite(MethodHandleNatives.java:296)                                                                                
        at invoke.InvokeExact0.main(InvokeExact0.java:41)                                                                                                                 
Caused by: java.lang.invoke.LambdaConversionException: Incorrect number of parameters for instance method invokeVirtual java.lang.invoke.MethodHandle.invokeExact:(MethodH
andle)Object; 0 captured parameters, 1 functional interface method parameters, 1 implementation parameters                                                                
        at java.lang.invoke.AbstractValidatingLambdaMetafactory.validateMetafactoryArgs(AbstractValidatingLambdaMetafactory.java:193)                                     
        at java.lang.invoke.LambdaMetafactory.metafactory(LambdaMetafactory.java:303)                                                                                     
        at java.lang.invoke.CallSite.makeSite(CallSite.java:289)                                                                                                          
        ... 2 more 

良いニュースは、メタファクトリ アプローチが機能する機能インターフェイス インスタンスを合成できたことです (印刷されているように: invoke.InvokeExact0$$Lambda$1/1878246837@1be6f5c3 )。悪いニュースは、メソッド参照アプローチが LambdaConversionException を引き起こし、それがさらに BootstrapMethodError を引き起こしたことです。

とにかくメタファクトリの回避策が存在するため、LambdaConversionException のエラーの詳細をどのように解釈することになっているのかを尋ねたいと思います。

4

1 に答える 1

9

手動で呼び出すコードは、メソッドハンドルが正しい署名metafactoryを持っている場合、メタファクトリがその仕事をすることを実際に示しています。MethodHandle.invokeExactデバッグにより、2 番目のケースでは、メソッド ハンドルがある(MethodHandle,MethodHandle)Objectべき場所に署名があることが明らかになり(MethodHandle)Objectました。

MethodHandle.invokeExactどちらも任意の署名を許可するので問題なく作成できますが(もちろん、最初の引数は である必要がMethodHandleあります)、スコープ内に 2 番目のメソッド ハンドルがないため、関数の署名と一致しないため、メタファクトリはハンドルを拒否します。

これは、メソッド ハンドル定数を生成したコンパイラのバグを示しています。一般に、非リフレクション コードがあり、リフレクション操作をInvokeExact0.new_(MethodHandle::invokeExact) 参照しているが、リフレクション操作を実行せずに実行時エラーが発生する場合は、コンパイラのバグを示しています。

簡単な回避策があります。その間

InvokeExact0<Object> ie=MethodHandle::invokeExact;

上記のエラーで失敗し、

InvokeExact0<Object> ie=mh -> mh.invokeExact();

期待どおりに動作します。次のように異なる戻り値の型が必要になるとすぐに、メソッド参照の代わりにラムダ式が必要になります。

InvokeExact0<String> ie=mh -> (String)mh.invokeExact();
于 2014-12-10T10:37:13.010 に答える