我有这种不寻常的(我认为?)情况
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{
}
}
现在我有一个扩展 Base 的类,并有一些带有签名的方法,如 executeAll 方法中所述,如下所示:
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) {
}
}
我知道这可以很容易地实现,我创建一个“受保护的摘要Collection<BiConsumer<Foo, Bar>> getToBeCalled()
”让每个孩子都扩展它并将 executeAll() 方法编写为:
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));
}
但这不是我想要达到的。我需要一种方法来从子类中执行方法,而无需开发人员进行任何重大干预(这就是为什么我考虑使用注释来标记希望执行的方法,仅此而已)。
现在我有一个工作实现,在 Base 空构造函数中使用 扫描每个可用的方法this.getClass().getMethods()
,过滤它们(必须有 Mark 注释,按顺序只接受 Foo 参数和 Bar 参数并返回 void,受保护)并将它们保存在aprivate List<Method> methods;
后来被 executeAll 用来调用每个方法。
最近我也读到了这个:https ://www.optaplanner.org/blog/2018/01/09/JavaReflectionButMuchFaster.html ...
...并且只是为了好玩实现了一个 MethodHandle 版本(我认为在我的用例中可能会更快),并计划制作一个 LambdaMetafactory 版本...但我真的对 javax.tools.JavaCompiler 很感兴趣解决方案。我仍在尝试弄清楚如何做到这一点...我保留了我原来的方法名称搜索和过滤,我只保留了名称...现在呢?如何将方法名称列表(我知道我可以访问,因为我从“this”调用并在子类中声明为受保护,并且还具有特定签名)转换为可用的东西,比如单个private final BiConsumer<Foo, Bar> callAll;
由 Base() 构造函数计算和保存,并由 executeAll 函数调用,如下所示:
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{
}
}
当然,使用 BiConsumer 作为 callAll 的想法只是一个想法,如果有更好的东西我完全可以接受建议。