Solved Abstract Listener Class

Discussion in 'Spigot Plugin Development' started by iHusker, Mar 10, 2020.

  1. Hello,

    I am currently working on creating a custom listener class but I have run into some issues. When the inherited class is initialized in main I get an error.

    AbstractListener Class
    Code (Java):
    public abstract class AbstractListener<E extends Event> implements Listener {

        @EventHandler
        public void onEvent(E event) {
            if (event instanceof PlayerEvent) {

                callEvent(event);

            }
        }

        public void initialise(Plugin plugin) {
            plugin.getServer().getPluginManager().registerEvents(this, plugin);
        }

        public abstract void callEvent(E event);
    }
    InheritedAbstractListener Class
    Code (Java):
    public class InheritedAbstractListener<PlayerJoinEvent> {

        @Override
        public void callEvent(PlayerJoinEvent event) {

        }
    }

    Error
    Code (Text):
    org.bukkit.plugin.IllegalPluginAccessException: Unable to find handler list for event org.bukkit.event.Event. Static getHandlerList method required!
     
  2. I'd guess using a generic type breaks Bukkit's event handler because it can't guarantee there's a static #getHandlerList method in that case.
     
  3. Generics in java are kind of just "syntactic sugar" in the sense that they do not exist at runtime. They are basically just a help for the compiler to assume objects to be of certain type at runtime so it knows things won't break. This is mostly due to backwards compatibility with older java programs since they didn't have generics at all. This is what's called type erasure. Basically, it will go down to the lowest possible type, which in this case is Event, since you've specified that as the lower bound of the generic parameter. If you want to check for yourself, startup jshell (if you have it) and call "InheritedAbstractListener.class.getMethods()" which will return - among others - the onEvent methods with one parameter of type Event.

    Simplest solution I would come up with is to create a protected method that receives the base event type and contains all the logic but does not have the event handler annotation. And then in each subtype you create a method that is basically just there to be an entry point for bukkit (so has EventHandler annotation and a concrete event type) as it just calls that protected method.

    (P.S.: If you notice the .getMethods() returns two callEvent methods; one with Event as a parameter type and one with the concrete PlayerJoinEvent type. Same rules apply here, which is why the base method still exists with the Event type, but since you overwrite it, you create an actual new method which has the concrete type. For java, this isn't the same method.)
     
    • Like Like x 1
    • Agree Agree x 1
  4. Why are you using E here
    Code (Text):
    public void onEvent(E event) {
    Just to check if it's a PlayerEvent afterwards? What you could listen for is Event event. Your abstract class can than ask for Class<E> as q constructor parameter.
    Now you can replace your instance of call to a
    Code (Text):
    theClassFromConstructor.isInstance(event)
     
  5. Shouldn't this be public class InheritedAbstractListener extends AbstractListener<PlayerJoinEvent>?
     
  6. I try this solution when I get home.