Почему мой метод isAnnotationPresent не работает, даже если Annotation имеет RetentionPolicy.RUNTIME? - PullRequest
1 голос
/ 03 ноября 2019

Я пытаюсь реализовать систему событий на основе аннотаций для моего игрового движка OpenGL. Я применяю аннотацию @EventListener к методу, который я хочу вызвать следующим образом:

@EventListener(type = Type.COLLISION)
public void OnCollision(CollisionEvent data)
{
    System.out.println("HI");
}

Класс, в котором находится этот метод, реализует пустой интерфейс:

public class Sprite implements EventHandler

EventDispatcherкласс:

public class EventDispatcher
{
private static List<EventHandler> registered = new ArrayList<EventHandler>();

public static void register(EventHandler EventHandler)
{
    if (!registered.contains(EventHandler))
    {
        registered.add(EventHandler);
    }
}

public static void unregister(EventHandler EventHandler)
{
    if (registered.contains(EventHandler))
    {
        registered.remove(EventHandler);
    }
}

public static List<EventHandler> getRegistered()
{
    return registered;
}

public static void dispatch(final Event event)
{
    new Thread()
    {
        @Override
        public void run()
        {
            call(event);
        }
    }.start();
}

private static void call(final Event event)
{
    for (EventHandler registered : getRegistered())
    {
        Method[] methods = registered.getClass().getMethods();

        for (int i = 0; i < methods.length; i++)
        {
            System.out.println("Annotation Being Checked");
            if (methods[i].isAnnotationPresent(EventListener.class))
            {
                System.out.println("Has Annotation");
                Class<?>[] methodParams = methods[i].getParameterTypes();
                if (methodParams.length < 1)
                {
                    continue;
                }
                if (!event.getClass().getSimpleName().equals(methodParams[0].getSimpleName()))
                {
                    continue;
                }
                try
                {
                    methods[i].invoke(registered.getClass().newInstance(), event);
                } catch (Exception exception)
                {
                    System.err.println(exception);
                }
            } else System.out.println("No Annotation");
        }
    }
}
}

Но когда я запускаю программу, она всегда печатает

Проверяемая аннотация
Нет аннотации

несколько раз.

Может кто-нибудь помочь? Если вам нужна дополнительная информация, пожалуйста, спросите, и я отредактирую Вопрос.

1 Ответ

0 голосов
/ 04 ноября 2019

Я настроил проект на основе вашего примера, и он работает нормально. Однако вы увидите некоторые сообщения «Без комментариев», поскольку ваш код оценивает все методы обработчика событий Sprite. Даже если вы не реализуете никаких дополнительных методов, кроме OnCollision, каждый класс будет наследовать методы по умолчанию от Object, такие как equals, hashCode или toString.

Тестовый класс :

public class SpriteTest {

    public static void main(String[] args) {
        EventDispatcher.register(new Sprite());

        CollisionEvent collisionEvent = new CollisionEvent();
        EventDispatcher.dispatch(collisionEvent);
    }
}



Кроме этого, в вашем коде есть некоторые очевидные недостатки:

  • Не используйте статические статические члены (EventDispatcher.registered)если вы не знаете, что делаете, и не знаете о многопоточности, которая идет с ним
  • Вы храните экземпляры EventHandler, но используете только информацию о классе и создаете новый экземпляр на лету - почему бы не зарегистрироваться? класс вместо экземпляра напрямую
  • Вы создаете новые потоки для каждого отправляемого события. Это очень плохая практика, так как создание потоков является дорогостоящей операцией. Вместо этого используйте пул потоков и отправьте runnables или callables
  • Вы проверяете, совпадают ли простые имена класса, чтобы увидеть, применим ли метод обработчика. Это сломается при использовании наследования и должно быть заменено на Class.isAssignableFrom
  • В целом использование аннотаций здесь сомнительно. Возможно, вам лучше использовать специальные интерфейсы для различных типов событий. Вместо общего EventHandler может быть CollisionEventHandler и так далее ...

Грубая идея реализации

public interface CollisionEventHandler extends EventHandler {
  void onCollision(CollisionEvent event);  
}

public class Sprite implements CollisionEventHandler {
  public void onCollision(CollisionEvent data) {
    System.out.println("HI");
  }
}

public class EventDispatcher {
  ...

  static void call(final CollisionEvent event) {
    getRegistered().stream()
          .filter(handler -> handler instanceof CollisionEventHandler)
          .map(handler -> (CollisionEventHandler) handler)
          .forEach(handler -> handler.onCollision(event));
  }
}

Для обработки различныхтипы событий вам понадобятся разные методы вызова / отправки. Может быть, вы можете использовать шаблон Visitor (хотя я не фанат этого).

...