How to resolve "AbstractMethodError" from LambdaMetafactory

I get AbstractMethodError

from calling the method defined by the call LambdaMetafactory#metafactory()

. I cannot figure out what I am doing wrong to trigger this. I have looked at several use cases LambdaMetafactory#metafactory()

online but have not found anything that exactly matches what I am trying to do.

Here's the [all] output of running the attached code:

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)

      

What I am trying to do is create a class that has one field that can be assigned a lambda directly, or assign a lookup to the class name and method name. The reason for the duality is to abstract from the way the method was called that was specified (either specified directly in the code or specified in the configuration file).

The nested code defines a functional interface ListMaker

using a method that creates a 1-item list from the string representation of the object. It contains a static method listify

that implements a function that matches the method signature of the interface and will be used for the part set by the set-the-method method.

Here's the code:

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);
  }
}

      

I was able to understand similar examples I found on the internet, but they are all end results; I haven't been able to find anything descriptive enough (for me, at least) about how the end results were obtained to help me figure out what I'm doing wrong.

+3


source to share


1 answer


I think the mistake is in using .generic()

in handle.type().generic()

when calling LambdaMetafactory.metafactory()

.

I took your code, removed the call .generic()

and your code worked successfully.



The documentation for the methodgeneric()

says that this method will convert all types to MethodType

, which is called in Object. h.type()

is a signature for a method that takes an object and returns a list, whereas h.type().generic()

is a signature for a method that takes an object and returns an object.

I don't think method generic()

has anything to do with generic types. Please don't feel like you need to use it just because the method in your functional interface has a generic parameter. I admit that I haven't come across this method before, but I think it has a confusing name.

+3


source







All Articles