【问题标题】:Java access bean methods with LambdaMetafactory带有 LambdaMetafactory 的 Java 访问 bean 方法
【发布时间】:2014-12-22 12:28:02
【问题描述】:

我的问题与Explicit use of LambdaMetafactory 密切相关 在该线程中,提供了一些非常好的示例来使用 LambdaMetafactory 访问类的静态方法;但是,我想知道访问现有 bean 实例的非静态字段的等效代码是什么。似乎真的很难找到一个例子,而且我执行的每一次尝试都以非工作代码告终。

这是 bean 代码:

class SimpleBean {
    private Object obj= "myCustomObject";
    private static Object STATIC_OBJECT = "myCustomStaticObject";
    public Object getObj() {
        return obj;
    }
    public void setObj(final Object obj) {
        this.obj = obj;
    }
    public static Object getStaticObj() {
        return STATIC_OBJECT;
    }
    public static void setStaticObj(final Object obj) {
        STATIC_OBJECT = obj;
    }
}

这是一个成功访问静态方法“getStaticObj()”的工作单元测试:

    @Test
public void accessStaticMethod() throws Throwable
{
    MethodHandles.Lookup caller = MethodHandles.lookup();
    Method reflected = SimpleBean.class.getDeclaredMethod("getStaticObj");
    MethodHandle methodHandle = caller.unreflect(reflected);
    CallSite site = LambdaMetafactory.metafactory(caller,
            "get",
            MethodType.methodType(Supplier.class),
            MethodType.methodType(Object.class),
            methodHandle,
            MethodType.methodType(Object.class));
    MethodHandle factory = site.getTarget();
    Supplier r = (Supplier) factory.invoke();
    assertEquals( "myCustomStaticObject", r.get());
}

现在我尝试访问非静态“getObj()”方法失败:

    @Test
public void accessNonStaticMethodTestOne() throws Throwable
{
    SimpleBean simpleBeanInstance = new SimpleBean();

    MethodHandles.Lookup caller = MethodHandles.lookup();
    MethodHandle methodHandle = caller.bind(simpleBeanInstance, "getObj", MethodType.methodType(Object.class));
    assertEquals("myCustomObject", methodHandle.invoke());

    // This test fails here with exception:
    // java.lang.IllegalArgumentException: not a direct method handle
    CallSite site = LambdaMetafactory.metafactory(caller,
            "get",
            MethodType.methodType(Supplier.class),
            MethodType.methodType(Object.class),
            methodHandle,
            MethodType.methodType(Object.class));

    MethodHandle factory = site.getTarget();
    Supplier r = (Supplier) factory.invoke();
    assertEquals( "myCustomObject", r.get());

}

@Test
public void accessNonStaticMethodTwo() throws Throwable
{

    SimpleBean simpleBeanInstance = new SimpleBean();

    MethodHandles.Lookup caller = MethodHandles.lookup();

    Method reflected = SimpleBean.class.getDeclaredMethod("getObj");
    MethodHandle methodHandle = caller.unreflect(reflected);

    // This test fails here with exception:
    // java.lang.invoke.LambdaConversionException: Incorrect number of parameters
    CallSite site = LambdaMetafactory.metafactory(caller,
            "get",
            MethodType.methodType(Supplier.class),
            MethodType.methodType(Object.class),
            methodHandle,
            MethodType.methodType(Object.class));

    MethodHandle factory = site.getTarget();
    factory = factory.bindTo(simpleBeanInstance);
    Supplier r = (Supplier) factory.invoke();
    assertEquals( "myCustomObject", r.get());

}


@Test
public void accessNonStaticMethodThree() throws Throwable
{

    SimpleBean simpleBeanInstance = new SimpleBean();

    MethodHandles.Lookup caller = MethodHandles.lookup();

    Method reflected = SimpleBean.class.getDeclaredMethod("getObj");
    MethodHandle methodHandle = caller.unreflect(reflected);

    CallSite site = LambdaMetafactory.metafactory(caller,
            "get",
            MethodType.methodType(Supplier.class),
            MethodType.methodType(Object.class, SimpleBean.class),
            methodHandle,
            MethodType.methodType(Object.class, SimpleBean.class));

    MethodHandle factory = site.getTarget();

    //This test fails here with exception:
    // java.lang.IllegalArgumentException: no leading reference parameter: spike.LambdaBeanAccessAtRuntimeTest$SimpleBean@4459eb14
    factory = factory.bindTo(simpleBeanInstance);
    Supplier r = (Supplier) factory.invoke();
    assertEquals( "myCustomObject", r.get());

}

每次尝试都有不同的阴性结果,我真的希望有人能帮助我让至少一个测试正常工作。

【问题讨论】:

  • 让我们看看你的尝试。解释您为什么尝试它们以及为什么它们不起作用。
  • 你有什么问题?
  • 我添加了示例代码。运行时抛出的异常会在测试代码中解释。
  • 我不知道你想做什么,但我敢打赌你使用了错误的工具。 LambdaMetafactory 是为专家用户(例如编译器编写者)提供的超级专业工具。
  • @BrianGoetz 我公司的一个内部 java 库在运行时大量使用反射来操作 bean。经过一些研究,我发现大多数反射基础代码都可以被运行时生成的 lambda 访问器替换。优点是执行速度与预编译代码一样快!

标签: java lambda java-8


【解决方案1】:

如果您想将值绑定到您的 Lamba,您必须将这些参数包含到调用类型签名中:

SimpleBean simpleBeanInstance = new SimpleBean();

MethodHandles.Lookup caller = MethodHandles.lookup();
MethodType getter=MethodType.methodType(Object.class);
MethodHandle target=caller.findVirtual(SimpleBean.class, "getObj", getter);
CallSite site = LambdaMetafactory.metafactory(caller,
    "get", // include types of the values to bind:
    MethodType.methodType(Supplier.class, SimpleBean.class),
    getter, target, getter);

MethodHandle factory = site.getTarget();
factory = factory.bindTo(simpleBeanInstance);
Supplier r = (Supplier) factory.invoke();
assertEquals( "myCustomObject", r.get());

您可以使用 Function 来代替绑定值,它将 bean 作为参数:

SimpleBean simpleBeanInstance = new SimpleBean();

MethodHandles.Lookup caller = MethodHandles.lookup();
MethodType getter=MethodType.methodType(Object.class);
MethodHandle target=caller.findVirtual(SimpleBean.class, "getObj", getter);
MethodType func=target.type();
CallSite site = LambdaMetafactory.metafactory(caller,
    "apply",
    MethodType.methodType(Function.class),
    func.erase(), target, func);

MethodHandle factory = site.getTarget();
Function r = (Function)factory.invoke();
assertEquals( "myCustomObject", r.apply(simpleBeanInstance));

【讨论】:

  • 嗨 Holger,你的工作对所有的 getter 来说都很好,它甚至可以使用原始参数。但是,我可以将它应用于 setter 方法。根据您的示例,我认为应该使用 BiConsumer 接口而不是 Function,我的尝试是:MethodType setter = MethodType.methodType(Void.TYPE, Object.class); MethodHandle target = caller.findVirtual(SimpleBean.class, "setObj", setter); MethodType func = target.type(); CallSite site = LambdaMetafactory.metafactory(caller, "accept",MethodType.methodType(BiConsumer.class), func.generic(), target, func); 工厂抛出和异常
  • 虽然 func.generic() 对于很多用例来说都很方便,但它不适用于带有 lambda 元工厂的 void 方法。所以你必须用func.changeParameterType(0, Object.class)替换func.generic()
猜你喜欢
  • 2021-11-03
  • 1970-01-01
  • 2010-09-08
  • 2010-09-07
  • 1970-01-01
  • 1970-01-01
  • 2012-09-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多