java中的MethodHandler入門


介紹

MethodHandler,翻譯過來就是方法句柄,是java7提供的jsr292的一部分,為了支持動態方法的調用,主要是java.lang.invoke包。

使用

public class Client {
  public static void main(String[] args) throws Throwable {
    MethodHandles.Lookup lookup = MethodHandles.lookup();
//獲取String類中靜態方法valueOf對應的方法句柄
    MethodHandle valueOfMethodHandler = lookup.findStatic(String.class, "valueOf", MethodType.methodType(String.class, int.class));
//執行方法句柄
    String result = (String) valueOfMethodHandler.invokeExact(12);
    System.out.println(result);
  }
}

Lookup可以簡單看做查找方法句柄的工具,MethodType表示一個方法類型,包括返回值和參數列表。方法句柄相對於反射來說,更加的輕量級。

動態調用點(CallSite)

java7新增了一個字節碼invokedynamic,可以在運行期動態決定調用的方法,區別於之前的invokestatic(靜態方法調用),invokespecial(構造方法,私有方法,父類方法),invokevirtual(實例方法),invokeinterface(接口方法),不過在java7下javac不支持生成invokedynamic,java8中可以通過lambda來生成。

public class Client {
  public static void main(String[] args) throws Throwable {
    MethodType type = MethodType.methodType(String.class, int.class, int.class);
    MethodHandles.Lookup lookup = MethodHandles.lookup();
    MethodHandle handle = lookup.findVirtual(String.class, "substring", type);
    ConstantCallSite callSite = new ConstantCallSite(handle);
    MethodHandle invoker = callSite.dynamicInvoker();
    String result = (String) invoker.invoke("Hello", 0, 3);
    System.out.println(result);
  }
}

以上一個CallSite的小例子,會輸出Hel。

public class Client {
  public static void main(String[] args) {
    updateAfterPay(info -> System.out.println(info));
  }

  private static void updateAfterPay(Consumer<String> consumer) {
    System.out.println("start ...");
    consumer.accept("pay success");
    System.out.println("end ...");
  }
}

這是lambda表達式的一個小例子,我們看一下反編譯后的結果

public class Client {
    public static void main(String[] args) {       Client.updateAfterPay((Consumer<String>)LambdaMetafactory.metafactory(null, null, null, (Ljava/lang/Object;)V, lambda$main$0(java.lang.String ), (Ljava/lang/String;)V)());
    }

    private static void updateAfterPay(Consumer<String> consumer) {
        System.out.println("start ...");
        consumer.accept("pay success");
        System.out.println("end ...");
    }
//這個方法是編譯器幫我們創建的,就是lambda處理的內容
    private static /* synthetic */ void lambda$main$0(String info) {
        System.out.println(info);
    }
}

看一下LambdaMetafactory的文檔介紹

Methods to facilitate the creation of simple "function objects" that
 * implement one or more interfaces by delegation to a provided {@link MethodHandle},
 * possibly after type adaptation and partial evaluation of arguments.  These
 * methods are typically used as <em>bootstrap methods</em> for {@code invokedynamic}
 * call sites, to support the <em>lambda expression</em> and <em>method
 * reference expression</em> features of the Java Programming Language.

可以簡單理解為一個創建動態調用點CallSite的工具,metafactory方法的返回值就是CallSite。
通過javap看一下反編譯的字節碼

從這我們可以看出lambda表達式的實現原理大概就是將lambda轉換成一個動態調用點的調用,動態調用點又會代理給方法句柄MethodHandle,在我們這個例子中就是lambda$main$0的方法句柄。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM