4

假设我有MyPerson一个带有namegetter 和 setter 的简单 javabean:

public class MyPerson {

    private String name;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

}

现在我正在运行这个简单地获取和设置该name字段的主代码:

    public static void main(String[] args) throws Throwable {
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        MethodHandle getterMethodHandle = lookup.findGetter(MyPerson.class, "name", String.class);
        MethodHandle setterMethodHandle = lookup.findSetter(MyPerson.class, "name", String.class);
        MyPerson a = new MyPerson();
        a.setName("Batman");
        System.out.println("Name from getterMethodHandle: " + getterMethodHandle.invoke(a));
        setterMethodHandle.invoke(a, "Robin");
        System.out.println("Name after setterMethodHandle: " + a.getName());
    }

如果我main()在 class 上添加该方法MyPerson,我会得到我所期望的:

Name from getterMethodHandle: Batman
Name after setterMethodHandle: Robin

如果我在另一个包中的另一个类上添加相同main()的方法,我会收到这个奇怪的错误:

Exception in thread "main" java.lang.NoSuchFieldException: no such field: batman.other.MyMain.name/java.lang.String/getField
    at java.lang.invoke.MemberName.makeAccessException(MemberName.java:875)
    at java.lang.invoke.MemberName$Factory.resolveOrFail(MemberName.java:990)
    at java.lang.invoke.MethodHandles$Lookup.resolveOrFail(MethodHandles.java:1373)
    at java.lang.invoke.MethodHandles$Lookup.findGetter(MethodHandles.java:1022)
    at batman.other.MyMain.main(MyMain.java:28)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Caused by: java.lang.NoSuchFieldError: name
    at java.lang.invoke.MethodHandleNatives.resolve(Native Method)
    at java.lang.invoke.MemberName$Factory.resolve(MemberName.java:962)
    at java.lang.invoke.MemberName$Factory.resolveOrFail(MemberName.java:987)

为什么?MyPerson的 getter/setter 是公开的,因此没有理由MyMain不使用它们,即使通过 MethodHandles。

将 JDK 8 与源/目标级别 java 8 一起使用。

4

2 回答 2

5

你正在混合这些东西。你需要的是:

MethodHandle getterMethodHandle = lookup.findVirtual(MyPerson.class,
        "getName", MethodType.methodType(String.class));
MethodHandle setterMethodHandle = lookup.findVirtual(MyPerson.class,
        "setName", MethodType.methodType(void.class, String.class));

findGetterand方法不会像在 Java Beans中findSetter那样尝试查找一些 getXXX 或 setXXX 方法。不要忘记方法句柄是非常低级的东西。这些方法实际上构建了一个方法句柄,它不指向现有方法,而只是设置具有给定名称的字段。使用findGetter你不需要在你的类中有一个实际的 getter 方法,但你必须可以直接访问该字段。如果你想使用 getter 方法getName,你仍然需要findVirtual调用。

一般来说,方法句柄比仅仅引用方法要强大得多。例如,您可以将方法句柄绑定到一个或多个参数,因此您无需在调用时指定它们。

于 2015-12-03T15:27:44.760 回答
1

如果您查看Lookup的文档,您将看到:

工厂方法创建的每个方法句柄都是特定字节码行为的功能等价物

例如 getter 查找:

lookup.findGetter(C.class,"f",FT.class)

被翻译成:

 (T) this.f;

因此出现了奇怪的错误NoSuchFieldError: name,因为您从外部引用了一个私有字段。对于 getter/setter,你应该选择findVirtual.

于 2015-12-03T15:34:06.337 回答