I have this unusual (I think?) situation
public abstract class Base {
public Base() {
// code
}
public final void executeAll() {
final Foo foo = new Foo();
final Bar bar = new Bar();
// now execute all method of "this" that:
// - have annotation mark
// - have return type void
// - accept (foo, bar) has parameters
// - name can be whatever you want
// - protected
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
protected @interface Mark{
}
}
Now I have a class that extends Base and has some methods with the signature as explained in the executeAll method, like this:
public class Test extends Base {
@Mark
protected void willBeCalled(Foo a, Bar b) {
}
@Mark
protected void alsoThisOne(Foo a, Bar b) {
}
@Mark
protected void yep(Foo a, Bar b) {
}
}
I know that this can be easily achieved my creating an "protected abstract Collection<BiConsumer<Foo, Bar>> getToBeCalled()
" make every child to extend it and write the executeAll() method as:
protected abstract Collection<BiConsumer<Foo, Bar>> getToBeCalled();
public final void executeAll() {
final Foo foo = new Foo();
final Bar bar = new Bar();
getToBeCalled().stream.forEach(bic-> bic.accept(foo, bar));
}
But this is not what I want to achieve. I need a way to get the methods to execute from the child class without any major intervention from the developer (that why I thought about an annotation to mark the method desired to be executed and nothing more).
Right now I have a working implementation that inside Base empty constructor scan for every available methods with this.getClass().getMethods()
, filter them (must have Mark annotation, accept only a Foo parameter and a Bar parameter in that order and return void, be protected) and save them in a private List<Method> methods;
later used by executeAll to call each method.
Recently I also read this: https://www.optaplanner.org/blog/2018/01/09/JavaReflectionButMuchFaster.html ...
... and just-for-fun implemented a MethodHandle version (I think that maybe in my use case will be faster), and planning to make a LambdaMetafactory version... but I'm really intrigued by the javax.tools.JavaCompiler solution. I'm still trying to figuring out how to do it tho... I keep my original method name search and filtering, I keep only the names... and now? How can I convert a list of method names (which I know I have access to, since I'm calling from "this" and are declared protected in the child class, and also have a specific signature) to something usable, like a single private final BiConsumer<Foo, Bar> callAll;
computed and saved by the Base() constructor and called by the executeAll function like this:
public abstract class Base {
private final BiConsumer<Foo, Bar> callAll;
protected Base() {
// get all methods <<< already done
Method[] methods = ;
// filter them by annotation, visibility and signature <<< already done
List<Method> filtered> = ;
// save only the names <<< already done
List<String> names = ;
// use javax.tools.JavaCompiler somehow <<< I miss this
callAll = ???;
}
public final void executeAll() {
final Foo foo = new Foo();
final Bar bar = new Bar();
callAll.accept(foo, bar);
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
protected @interface Mark{
}
}
Of course the idea to use a BiConsumer as callAll is just an idea if there is something better I'm perfectly fine with suggestion.