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.

0

There are 0 best solutions below