I'm getting an AbstractMethodError
from invoking a method defined by a call to LambdaMetafactory#metafactory()
. I can't figure out what I'm doing wrong to cause it. I've looked at quite a few examples of using LambdaMetafactory#metafactory()
online, but haven't found anything that exactly matches what I'm trying to do.
Here's the [entire] 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'm trying to do is create a class that has a single field that can be assigned a lambda directly, or be assigned by a lookup of a class name and method name. The reason for the duality is to abstract away the way in which the method being invoked was specified (either specified directly in code, or specified in a configuration file).
The attached code defines a functional interface ListMaker
with a method that produces a 1-element list from the string representation of an object. It contains a static method listify
that implements a function matching the interface's method's signature, and will be used for the set-the-method-directly portion of the sample.
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've been able to understand the similar examples I've found online, but they are all end-results; I haven't been able to find anything descriptive enough (for me, at least) on how the end-results were derived to help me figure out what I'm doing wrong.
,Object>` as functional interface, but as soon as primitive types or other bounds than `Object` come into play, it is inappropriate.
– Holger Jul 13 '15 at 18:31