CoderMusgrove
CoderMusgrove

Reputation: 614

Java: Pass actual <T extends Event> class. (Generics Problems)

I've made my own EventExecutor class, which has the type <T extends Event>.

public interface EventExecutor<T extends Event> {

    ...
    public abstract void execute(Event event);
}

and I know right now that this up here is wrong, because I'm wanting to pass in:

public abstract void execute(T event);

I've been working on an Event Management System, and when an Event isn't cancelled after all other events have ran, certain code will run from the EventExecutor as shown:

// event parameter should be of type T.
EventManager.call(event -> {
    // code here.
}, Class<? extends Event>, Object... eventParameters);

The problem here is since the EventExecutor method execute has the parameter of Event, that means event -> is of type Event, and not the wanted class that extends Event, so that means I have to do some casting, which isn't what I want to do.

The problem that keeps me from using the generic type of T is my EventManager class's method call:

public static void call(EventExecutor<? extends Event> eventExecutor, Class<? extends Event> eventClass, Object... eventArgs) {
    synchronized (LOCK) {
        if (!checkIsEventClassRegistered(eventClass)) return;
        try {
            Event event = null;

            Class<?>[] constructorParameters = EventUtilities.getArrayOfClasses(eventArgs);
            Constructor<?> constructor = eventClass.getDeclaredConstructor(constructorParameters);

            event = (Event) constructor.newInstance(eventArgs);

            // HERE:
            if (!callAllRegisteredMethods(event)) eventExecutor.execute(event);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

I'll admit, I'm new to generics, and I don't see what's so wrong with everything. I've tried casting event (in the eventExecutor.execute(...) method) to T, but it tells me to create the method in EventExecutor called eventExecutor.execute(T), but then EventExecutor throws an error, because it wants T to be a class that I create in the same package.

What am I doing wrong?

Edit
So basically, in the end I want to be able to not have to cast to my wanted Event class:

EventManager.call(event -> {
    // Have this automatically be recognized as an instance of MyEventClass?
    System.out.println(event.getText());
}, MyEventClass.class, new String("text"));

Upvotes: 0

Views: 458

Answers (1)

NamshubWriter
NamshubWriter

Reputation: 24286

Can you make call a generic method?

public static <T extends Event> void call(
   EventExecutor<T> eventExecutor, Class<T> eventClass, Object... eventArgs) {
  synchronized (LOCK) {
    if (!checkIsEventClassRegistered(eventClass)) return;
    try {
      Class<?>[] constructorParameters = EventUtilities.getArrayOfClasses(
          eventArgs);
      Constructor<?> constructor = eventClass.getDeclaredConstructor(
          constructorParameters);

      T event = eventClass.cast(constructor.newInstance(eventArgs));

      if (!callAllRegisteredMethods(event)) eventExecutor.execute(event);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

Note I replaced the cast to a call to Class.cast().

Edit: The only reason why EventExecutor has to be generic is because you want to pass in an anonymous function/class to do the execution, and you want that function/class to depend on the actual type of the event. To avoid confusion, I would call it an EventCallback, since EventExecutor sounds similar to java.util.concurrent.Executor, so I was left wondering why you would want more than one.

Incidentally, it's probably not a good idea to call so much code that isn't under your control while you are in a synchronized block.

Upvotes: 1

Related Questions