Reputation: 605
Java 11, Spring Boot and Lombok here. I am trying to build up a hierarchy of objects extending Spring's ApplicationEvent
class which forces me to provide a constructor:
public ApplicationEvent(Object source) {
...
}
So extending ApplicationEvent
I have a TrackedEvent
that is an abstract base class for my other events to build off:
public abstract class TrackedEvent extends ApplicationEvent {
private String correlationId;
private String description;
// ... lots of fields here
}
One of the (many) events extending TrackedEvent
is OperationalEvent
:
public class OperationalEvent extends TrackedEvent {
private OperationType type;
private OperationStatus status;
// ... several more fields here
}
Other than the source : Object
field, which is required by the grandparent ApplicationEvent
constructor, all other TrackedEvent
and OperationalEvent
fields are optional. So because there are so many fields here to populate and instantiation-time, I feel the Builder Pattern is appropriate. So I'd like to employ Lombok's @Builder
annotation (and possibly other annos) to obtain an API something like so:
OperationalEvent opsEvent = OperationalEvent.Builder(this)
.description("something happened")
.status(OperationalStatus.THE_THING)
.build();
My thinking here is we would pass this
in as a required builder constructor argument (to satisfy ApplicationEvent
's constructor for source : Object
...somehow) and then use the @Builder
like normal.
Is this possible to do in Lombok, and if so, how?
As a fallback I can just write my own builders but since I have so many subclasses it would be great to leverage Lombok if at all possible.
Upvotes: 6
Views: 4083
Reputation: 8042
You can use @SuperBuilder
together with an intermediate abstract class, as described here.
The advantage over using customized @Builder
s is that you only have to manually implement this single custom intermediate class once. There is no need for customization in every subclass.
In this case, the intermediate class looks as follows:
public abstract class ApplicationEventSuperBuilderEnabler extends ApplicationEvent {
public static abstract class ApplicationEventSuperBuilderEnablerBuilder<C extends ApplicationEventSuperBuilderEnabler, B extends ApplicationEventSuperBuilderEnablerBuilder<C, B>> {
private Object source;
public B source(Object source) {
this.source = source;
return self();
}
protected abstract B self();
public abstract C build();
}
protected ApplicationEventSuperBuilderEnabler(ApplicationEventSuperBuilderEnablerBuilder<?, ?> b) {
super(b.source);
}
}
Next, let all your direct subclasses extend ApplicationEventSuperBuilderEnabler
(instead of ApplicationEvent
) and put a @SuperBuilder
on all classes:
@SuperBuilder
public abstract class TrackedEvent extends ApplicationEventSuperBuilderEnabler { ... }
@SuperBuilder
public class OperationalEvent extends TrackedEvent { ... }
Now you can use it like this:
OperationalEvent opsEvent = OperationalEvent.builder()
.source(this)
.description("something happened")
.status(OperationalStatus.THE_THING)
.build();
Upvotes: 1
Reputation: 3659
Since you can't use @SuperBuilder
annotation, beacause you don't have access to ApplicationEvent
class, as far as I can see, the only solution is to do something like following:
Assume that you have TrackedEvent
class wich looks something like this:
public class TrackedEvent extends ApplicationEvent {
protected String correlationId;
protected String description;
public TrackedEvent(Object source, String correlationId, String description) {
super(source);
this.correlationId = correlationId;
this.description = description;
}
public TrackedEvent(Object source) {
super(source);
}
}
And you have OperationalEvent
class wich extends above class.
Than, you can implement builder pattern in OptionalEvent class like this:
public class OperationalEvent extends TrackedEvent {
private String type;
private String status;
public OperationalEvent(Object source) {
super(source);
}
@Builder(builderMethodName = "optionalEvent")
public OperationalEvent(Object source, String correlationId, String description, String type, String status) {
super(source, correlationId, description);
this.type = type;
this.status = status;
}
@Override
public String toString() {
return "OperationalEvent{" +
"type='" + type + '\'' +
", status='" + status + '\'' +
"correlationId:" + correlationId +
'}';
}
}
And than the following code should work as expected:
public static void main(String[] args) {
ApplicationEvent applicationEvent = new ApplicationEvent(new Object()) {
@Override
public Object getSource() {
return super.getSource();
}
};
OperationalEvent opsEvent = OperationalEvent.optionalEvent()
.description("something happened")
.source(applicationEvent)
.status("status")
.correlationId("1")
.build();
}
Upvotes: 4