Reputation: 71
Considering the following code:
public class MyTest {
public void sayHello(Supplier<String> myGetter) {
System.out.println("Hello " + myGetter.get());
}
public static void main(String[] args) {
Person person = new Person("Michal", "Lefler");
MyTest myTest = new MyTest();
List<Supplier<String>> suppliers = new ArrayList<>();
suppliers.add(person::getName);
suppliers.add(person::getLastName);
suppliers.forEach(myTest::sayHello);
}
static class Person {
private String name;
private String lastName;
public Person(String name, String lastName) {
this.name = name;
this.lastName = lastName;
}
@NameMethod
public String getName() {
return name;
}
@NameMethod
public String getLastName() {
return lastName;
}
public String getBlahBlah() {
return "BlahBlah";
}
public String getPakaPaka() {
return "PakaPaka";
}
}
@interface NameMethod {}
}
My question is: Instead of filling the suppliers list explicitly:
suppliers.add(person::getName);
suppliers.add(person::getLastName);
I would like to fill the suppliers list automatically, using the "@NameMethod" annotation. I mean that I would like to scan all the "@NameMethod" annotated methods, using reflection, and then to somehow add these methods to the suppliers list. Is there a way I can do it? How? Thanks.
Upvotes: 4
Views: 2532
Reputation: 71
Thanks! Both answers were very helpful. I would like to suggest my solution, based on yours, but avoid using reflection for the routine method invocation (we have performance restrictions). Instead, I'm using "MethodHandle" objects.
public class MyTest {
public void sayHello(Supplier<String> myGetter) {
System.out.println("Hello " + myGetter.get());
}
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException {
Person person = new Person("Michal", "Lefler");
MyTest myTest = new MyTest();
List<Supplier<String>> suppliers = new ArrayList<>();
Method[] methods = Person.class.getMethods();
for (Method method : methods) {
if (!method.isAnnotationPresent(NameMethod.class))
continue;
MethodType desc = MethodType.methodType(String.class);
MethodHandle methodHandle = MethodHandles.lookup().findVirtual(
Person.class, method.getName(), desc);
suppliers.add(new NameSupplier<>(methodHandle, person));
}
suppliers.forEach(myTest::sayHello);
}
static class Person {
private String name;
private String lastName;
public Person(String name, String lastName) {
this.name = name;
this.lastName = lastName;
}
@NameMethod
public String getName() {
return name;
}
@NameMethod
public String getLastName() {
return lastName;
}
public String getBlahBlah() {
return "BlahBlah";
}
public String getPakaPaka() {
return "PakaPaka";
}
}
@Retention(value= RetentionPolicy.RUNTIME)
@interface NameMethod {}
public static class NameSupplier<T> implements Supplier<String> {
private final MethodHandle methodHandle;
private final T object;
public NameSupplier(MethodHandle methodHandle, T object) {
this.methodHandle = methodHandle;
this.object = object;
}
@Override
public String get() {
try {
return (String) methodHandle.invoke(object);
} catch (Throwable throwable) {
throw new RuntimeException(throwable);
}
}
}
}
Upvotes: 1
Reputation: 70
A Supplier is just an interface that you can implement.
public static class NameSupplier implements Supplier<String> {
private final Method method;
private final Object object;
public NameSupplier(Method method, Object object) {
this.method = method;
this.object = object;
}
@Override
public String get() {
try {
return (String) method.invoke(object);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
If you use reflection, you can get all methods that are annotated with @NameMethod
public static void main(String[] args) {
Person person = new Person("Michal", "Lefler");
MyTest myTest = new MyTest();
List<Supplier<String>> suppliers = new ArrayList<>();
Method[] methods = Person.class.getMethods();
for (Method method : methods) {
if (!method.isAnnotationPresent(NameMethod.class))
continue;
suppliers.add(new NameSupplier(method, person));
}
suppliers.forEach(myTest::sayHello);
}
Upvotes: 1
Reputation: 533530
You can use reflection as you suggest and check for the annotation and create a supplier for each method.
Person p = ...
for (Method m : Person.class.getMethods())
if (m.getAnnotation(NameMethod.class) != null)
suppliers.add(() -> {
try {
return m.invoke(p);
} catch (Exception e) {
throw new AssertionError(e);
}
});
Upvotes: 2