So I'm creating a library that allows users to pass a Class<?>
and collect all static methods with a specific annotation (and other criteria, such as a certain parameter count and types) and convert them into lambda FunctionalInterfaces
that my library will use internally for processing.
For example:
Say I have the following class tree:
public abstract class AbstractParent { public String sayHi() { return getClass().getName() +" instance says hi!"; }}
with subclasses:
public class ChildOne extends AbstractParent { public int childOneSpecialMethod() { return 2558445; }}
public class ChildTwo extends AbstractParent { public int childTwoSpecialMethod() { return 484848; }}
My library allows for users to annotate a class's static methods with the following annotation:
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public @interface ProcessAnnotation {}
with the following rules: the static method first parameter be an instance of AbstractParent
, and its second parameter must be a String
, and must return a String
. So, something like this:
public class GeneralProcessor { @ProcessAnnotation public static String easyProcessing(ChildOne one, String otherArg) { //Some code System.out.println(" === In processing for ChildOne types"); return otherArg + one.toString(); } @ProcessAnnotation public static String easyProcessing(ChildTwo two, String otherArg) { //Some code System.out.println(" === In processing for ChildTwo types"); return otherArg + two.toString(); }}
On the library-side of things, I want to collect all these methods so that I can use them for some processing while doing it in a relatively fast manner and I found that MethodHandles
and LambdaMetaFactory
is the best way to do this.
Specifically, I want to invoke these collected methods using my own FunctionalInterface
:
@FunctionalInterfacepublic interface ProcessInterface<T extends AbstractParent> { public String process(T obj, String extraArg);}
So far, what I've tried is something like this:
public static List<ProcessInterface<? extends AbstractParent>> generate(Class<?> targetClass) throws Throwable { ArrayList<ProcessInterface<? extends AbstractParent>> processors = new ArrayList<>(); for (Method method : targetClass.getDeclaredMethods()) { if (method.isAnnotationPresent(ProcessAnnotation.class) && method.getParameterCount() == 2 && AbstractParent.class.isAssignableFrom(method.getParameterTypes()[0]) && method.getParameterTypes()[1] == String.class) { MethodHandles.Lookup lookup = MethodHandles.lookup(); MethodHandle handle = lookup.unreflect(method); CallSite callSite = LambdaMetafactory.metafactory(lookup, "process", MethodType.methodType(ProcessInterface.class), MethodType.methodType(String.class, method.getParameterTypes()[0], String.class), handle, handle.type()); ProcessInterface<? extends AbstractParent> func = (ProcessInterface<? extends AbstractParent>) callSite.getTarget().invoke(); processors.add(func); } } return processors; }
However, I get the following error when I actually invoke the lambda. For example:
List<ProcessInterface<? extends AbstractParent>> interfaces = generate(GeneralProcessor.class);ChildOne childOne = new ChildOne();interfaces.get(0).process(childOne, "");
Is there a fix to do this? Or maybe even a better way to achieve this?