2016-02-10 3 views
2

Учитывая следующий код:Объединение Java 8 Lambda с аннотациями

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 {} 
} 

Мой вопрос: Вместо заполнения поставщиков список в явном виде:

suppliers.add(person::getName); 
    suppliers.add(person::getLastName); 

Я хотел бы, чтобы заполнить список поставщиков автоматически , используя аннотацию «@NameMethod». Я имею в виду, что я хотел бы отсканировать все аннотированные методы «@NameMethod», используя отражение, а затем как-то добавить эти методы в список поставщиков. Есть ли способ, которым я могу это сделать? Как? Спасибо.

+1

Вы уже подумали об этом и нашли приемлемое решение, и в чем же проблема? Что блокирует вас от написания кода? –

+0

Я * не нашел решение. Что заставляет вас думать, что я это сделал? Я не знаю, как объединить аннотацию @NameMethod с лямбда-выражениями. –

+0

Да, вы сделали: «Я бы хотел отсканировать все аннотированные методы @NameMethod, используя отражение, а затем как-то добавить эти методы в список поставщиков». Это решение. Опять же, что блокирует вас от кодирования? Вот о чем должен быть ваш вопрос: что мешает вам идти дальше. –

ответ

2

Вы можете использовать отражение, как вы предлагаете, и проверить аннотацию и создать поставщика для каждого метода.

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); 
      } 
     }); 
1

Поставщик - это просто интерфейс, который вы можете реализовать.

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); 
     } 
    } 
} 

Если вы используете отражение, вы можете получить все методы, которые аннотированный с @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); 
} 
0

Спасибо! Оба ответа были очень полезными. Я хотел бы предложить свое решение, основанное на вашем, но не использовать отражение для рутинного вызова метода (у нас есть ограничения производительности). Вместо этого я использую объекты MethodHandle.

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); 
     } 
    } 
} 
} 
+0

Вы можете запустить отражающий код при инициализации класса и кэшировать результат. Таким образом, он будет запускаться только один раз. Класс не изменится во время выполнения. –

+0

Следует, вероятно, отметить вопрос как ответ –

Смежные вопросы