# 方法句柄

## Intro

Java通过反射可以在运行时动态获取或修改类型字段、方法等信息，但反射的执行速度也成一大诟病。Java7开始提供另一套API称为方法句柄（`Method Handle`），其作用与反射类似，但执行效率比反射高，被称为现代化反射。

> A method handle is a typed, directly executable reference to an underlying method, constructor, field, or similar low-level operation, with optional transformations of arguments or return values.

方法句柄是一种指向方法的强类型、可执行的引用，它的类型由方法的参数类型和返回值类型组成，而与方法所在类名和方法名无关。 它作为方法的抽象，使方法调用不再受所在类型的约束，能更好的支持动态类型语言特性。

几个关键的类：

* Lookup：方法句柄的创建工厂类，方法的检查工作是在创建时处理的而非调用时处理
* MethodType：方法的签名，包括返回值类型和参数类型
* MethodHandle：方法句柄，用于动态访问类型的信息

### Lookup

创建一个提供对公共方法访问的查找：

```java
MethodHandles.Lookup publicLookup = MethodHandles.publicLookup();
```

`lookup`方法可访问私有和受保护的方法

```java
MethodHandles.Lookup lookup = MethodHandles.lookup();
```

`Lookup`对象提供几种查找对象的方法

* invokevirtual

对象方法的查找

```java
public MethodHandle findVirtual(Class<?> refc, String name, MethodType type)
```

* invokestatic

静态方法查找

```java
public MethodHandle findStatic(Class<?> refc, String name, MethodType type)
```

* 查找构造函数

```java
public MethodHandle findConstructor(Class<?> refc, MethodType type)
```

* 查找非私有字段

```java
/**
* Produces a method handle giving read access to a non-static field.
* The type of the method handle will have a return type of the field's
* value type.
* The method handle's single argument will be the instance containing
* the field.
*/
public MethodHandle findGetter(Class<?> refc, String name, Class<?> type)
```

```java
/**
* Produces a method handle giving write access to a non-static field.
* The type of the method handle will have a void return type.
 * The method handle will take two arguments, the instance containing
* the field, and the value to be stored.
*/
public MethodHandle findSetter(Class<?> refc, String name, Class<?> type)
```

### MethodHandle

使用`invoke`家族来调用

`invoke`、`invokeWithArguments`、`invokeExact`

### MethodType

`Lookup`需要方法的签名才能找到方法句柄

```java
public static MethodType methodType(Class<?> rtype, Class<?> ptype0, Class<?>... ptypes)
```

`rtype`为返回值类型，`ptypes`为参数类型

## QuickStart

```java
class Horse {
  public void race() {
    System.out.println("Horse.race()"); 
  }
}

class Deer {
  public void race() {
    System.out.println("Deer.race()");
  }
}

class Cobra {
  public void race() {
    System.out.println("How do you turn this on?");
  }
}
```

如何设计统一的方式调用`race`方法？

使用反射有性能损耗，抽象出一个包含`race`方法的接口则增加了接口约束，这时候就可以考虑方法句柄了。将`race`方法抽象成方法句柄，它们的句柄类型一致，对调用者暴露方法句柄即可。

```java
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;

public class MethodHandleTest {
    public void race(Object obj) throws Throwable {
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        MethodHandle race = lookup.findVirtual(obj.getClass(), "race", MethodType.methodType(void.class));
        race.invoke(obj);
    }

    public static void main(String[] args) throws Throwable {
        MethodHandleTest test = new MethodHandleTest();
        test.race(new Horse());
        test.race(new Deer());
    }
}
```

方法句柄也有权限问题，但与反射在方法调用时检查不同，它是在**句柄创建阶段进行检查**的，如果多次调用句柄，它比反射可以省下权限重复检查的开销。

但需注意的是，**句柄的访问权限不取决于创建句柄的位置，而是 Lookup 对象的位置**。

## Runtime#exec

```java
MethodHandles.Lookup lookup = MethodHandles.lookup();

MethodType mt1 = MethodType.methodType(Runtime.class);
MethodHandle getRuntime = lookup.findStatic(Runtime.class, "getRuntime", mt1);

MethodType mt2 = MethodType.methodType(Process.class, String.class);
MethodHandle exec = lookup.findVirtual(Runtime.class, "exec", mt2);

Object runTime = getRuntime.invokeWithArguments();
exec.invoke(runTime, "calc");
```

`getRuntime`没有参数，对应的`MethodType`只要传一个`Runtime.class`即可


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://p4d0rn.gitbook.io/java/prerequisites/fan-she/methodhandle.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
