【问题标题】:How to resolve "AbstractMethodError" from LambdaMetafactory如何从 LambdaMetafactory 解决“AbstractMethodError”
【发布时间】:2015-05-30 21:57:58
【问题描述】:

我通过调用LambdaMetafactory#metafactory() 定义的方法得到AbstractMethodError。我无法弄清楚我做错了什么导致它。我在网上查看了很多使用LambdaMetafactory#metafactory() 的示例,但没有找到与我正在尝试做的完全匹配的任何示例。

这是运行附加代码的[整个]输出:

Result[0] = "version 1"
Result[0] = "1"
Exception in thread "main" java.lang.AbstractMethodError
    at junk.LMTest.invokeMaker(LMTest.java:52)
    at junk.LMTest.main(LMTest.java:65)

我要做的是创建一个具有单个字段的类,该字段可以直接分配一个 lambda,或者通过查找类名和方法名来分配。双重性的原因是抽象出指定被调用方法的方式(直接在代码中指定,或在配置文件中指定)。

附加的代码定义了一个功能接口ListMaker,该接口具有一个从对象的字符串表示生成单元素列表的方法。它包含一个静态方法listify,它实现了一个与接口方法签名匹配的函数,并将用于示例的直接设置方法部分。

代码如下:

package junk;

import java.lang.invoke.CallSite;
import java.lang.invoke.LambdaMetafactory;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class LMTest
{

  @FunctionalInterface
  public interface ListMaker
  {
    public List<String> makeList(Object obj);
  }

  private ListMaker maker;

  public static List<String> listify(Object obj)
  {
    List<String> l = new ArrayList<>();
    l.add(obj.toString());
    return l;
  }

  public void setMaker(ListMaker maker)
  {
    this.maker = maker;
  }

  public void setMaker(String className, String methodName)
      throws Throwable
  {
    Method m = Class.forName(className).getDeclaredMethod(methodName, Object.class);
    MethodHandles.Lookup l = MethodHandles.lookup();
    MethodHandle handle = l.unreflect(m);
    CallSite cs = LambdaMetafactory.metafactory(l,
                                                "makeList",
                                                MethodType.methodType(ListMaker.class),
                                                handle.type().generic(),
                                                handle,
                                                handle.type());
    maker = (ListMaker)cs.getTarget().invoke();
  }

  public void invokeMaker(Object obj)
  {
    String result0 = maker.makeList(obj).get(0);
    System.out.println("Result[0] = \"" + result0 + "\"");
  }

  public static void main(String[] args)
      throws Throwable
  {
    LMTest lmt = new LMTest();
    lmt.setMaker(LMTest::listify);
    lmt.invokeMaker("version 1");
    lmt.invokeMaker(1);
    //
    lmt.setMaker("junk.LMTest", "listify");
    lmt.invokeMaker("version 2");
    lmt.invokeMaker(2);
  }
}

我在网上找到的类似示例我已经能够理解,但它们都是最终结果;我无法找到任何描述性足够的内容(至少对我而言)最终结果是如何得出的,以帮助我找出我做错了什么。

【问题讨论】:

    标签: java lambda method-reference


    【解决方案1】:

    我认为错误是在您对LambdaMetafactory.metafactory() 的调用中使用了handle.type().generic() 中的.generic()

    我拿走了你的代码,去掉了对.generic()的调用,你的代码运行成功了。

    documentation for the generic() method 表示此方法将调用它的 MethodType 中的所有类型转换为 Object。 h.type() 是接受 Object 并返回 List 的方法的签名,而 h.type().generic() 是接受 Object 并返回 Object 的方法的签名。

    我不认为generic() 方法与泛型类型有任何关系。请不要仅仅因为函数式接口中的方法有一个泛型类型的参数就觉得你必须使用它。我承认我以前没有遇到过这种方法,但我认为它的名称令人困惑。

    【讨论】:

    • 谢谢。你准确地解决了我的误解。如果您碰巧知道任何描述使用 LambdaMetafactory 的不同方法的文档/教程,除了 [通常是神秘的] API 文档,请发布。这会很有帮助。
    • @CraftWeaver: 如果你收到来自here.generic() 调用,我必须解释它适用于像Function 这样具有原始签名的通用功能接口,其中所有参数和返回类型是Object,但没有别的。所以它派上用场了,而且它起作用了,如果你使用 Function&lt;List&lt;String&gt;,Object&gt; 作为函数接口,但是一旦原始类型或除 Object 之外的其他边界开始发挥作用,它就是不合适的。
    猜你喜欢
    • 1970-01-01
    • 2015-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多