Skip to content

Conversation

PaintNinja
Copy link
Contributor

#69 mentioned the following possible future enhancement:

Direct pass-through of sealed inheritable events that only have one subclass to save memory and reduce startup time.

Consider the following example:

// in a public exported API package
public sealed interface MyEvent extends Cancellable, InheritableEvent permits MyEventImpl {
    CancellableEventBus<MyEvent> BUS = CancellableEventBus.create(MyEvent.class);

    String message();
}

// in an internal package
public record MyEventImpl(String message) implements MyEvent {
    public static final CancellableEventBus<MyEventImpl> BUS = CancellableEventBus.create(MyEventImpl.class);
}

The BUS fields currently refer to different EventBus instances, although they effectively do the same thing - regardless of whether you add a listener to the parent or the child, and regardless of whether you post the MyEventImpl instance on the parent's bus or the child's.

This PR makes the CancellableEventBus.create(MyEventImpl.class); call return the bus of the parent instead of creating a new instance, reducing memory usage by sharing the same instance across both classes.

Direct pass-through of sealed inheritable events that only have one subclass to save memory and reduce startup time.
@PaintNinja PaintNinja added the enhancement New feature or request label Sep 19, 2025
@PaintNinja PaintNinja marked this pull request as ready for review September 19, 2025 15:24
@LexManos
Copy link
Member

Honestly I'd be more inclined to have MyEventImpl fire on MyEvent.BUS, not even using its own BUS field.
However, on the technical side I just want to be sure that cases such as the following still work.
Sealed class with one permits, final class with separate bus, both events fired, Parent called twice, child called once.

// in a public exported API package
public sealed class DogEvent extends Cancellable, InheritableEvent permits LabradorEvent {
    CancellableEventBus<DogEvent> BUS = CancellableEventBus.create(DogEvent.class);
}

public final class LabradorEvent extends DogEvent {
    public static final CancellableEventBus<LabradorEvent> BUS = CancellableEventBus.create(LabradorEvent.class);

    private final String type;
    public LabradorEvent(String type) {
        this.type = type;
    }    
    
    public String getType() { return this.type; }
}

new DogEvent().post();
new LabradorEvent("golden").post();

@Jonathing
Copy link
Member

The internal code is a little hard to grasp, but I understand what's going on here. So to put it in list form:

If:

  • An event is inheritable
  • It is sealed
  • It only permits a single subtype

Then:

  • Delegate to only using the parent's bus if it exists.

But I agree with what Lex is saying here as well. It should be better to just point to the parent's event bus. However, if and only if it has access to it. I can see the case where you want to hide the bus from consumers by grabbing the parent instead.

So I'm unsure about the practicality of this. But the internal code looks sane to me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants