У меня такая необычная (я думаю?) Ситуация
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));
}
Но это это не , чего я хочу достичь. Мне нужен способ получить методы для выполнения из дочернего класса без какого-либо серьезного вмешательства со стороны разработчика (вот почему я подумал о аннотации, чтобы отметить метод, который нужно выполнить, и ничего более).
Прямо сейчас У меня есть работающая реализация, которая внутри Базового пустого конструктора сканирует все доступные методы с this.getClass().getMethods()
, фильтрует их (должен иметь аннотацию Mark, принимать только параметр Foo и параметр Bar в этом порядке и возвращать void, быть защищенным) и сохранять их в private List<Method> methods;
, позже использованном executeAll для вызова каждого метода.
Недавно я также прочитал это: https://www.optaplanner.org/blog/2018/01/09/JavaReflectionButMuchFaster.html ...
... и просто - for-fun реализовал версию MethodHandle (я думаю, что, возможно, в моем случае использования будет быстрее), и планирую сделать версию LambdaMetafactory ... но я действительно заинтригован решением javax.tools.JavaCompiler. Я все еще пытаюсь выяснить, как это сделать, хотя ... Я продолжаю свой поиск и фильтрацию названий методов, я сохраняю только имена ... и сейчас? Как я могу преобразовать список имен методов (к которому я знаю, что у меня есть доступ, так как я звоню из "this" и объявлен защищенным в дочернем классе, а также определен c сигнатура) для чего-то пригодного для использования, например, одного 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 - это просто идея, если есть что-то лучшее, я совершенно согласен с предложением.