build method? Suppose I want to change some values (e.g. trim them) before building. I think calling private custom postBuild method will be a good solution for this problemIf you declare the builder class yourself you can do all sorts of things.
I would try to avoid mucking with generated build methods directly if I needed to modify the values I would do that with setters..
public static interface WithNumericValue {
Number getValue();
public static interface WithNumericValueSetter<BLDR extends WithNumericValue.WithNumericValueSetter<BLDR, VALUE>, VALUE extends Number> {
BLDR value(VALUE value);
}
}
@Value
@Builder(toBuilder = true)
public static class MyClass implements WithNumericValue {
private Double value;
public static class MyClassBuilder implements
WithNumericValue.WithNumericValueSetter<MyClass.MyClassBuilder, Double> {
@Override
public MyClassBuilder value(Double value) {
this.value = value;
System.out.println("Set value to:" + value);
return this;
}
}
}
Simplified solution:
@Builder
@Getter
public class Abc {
private String title;
private Date createdOn;
public static class AbcBuilder {
public AbcBuilder title(String title) {
this.title = title.trim();
return this;
}
}
}
But still... is it possible to execute custom logic before building? Suppose I want to print message before object is build
I have a use-case that is similar to this.
In general, the builder interface might not be the same as the value interface. An example of this:
class ClassWithId {
@Pattern("[^:]+:[^:]+:.*") String id;
}
class Person extends ClassWithId {
Person(String firstIdSection, String lastIdSection) {
this.id = firstIdSection + ":person:" + lastIdSection;
}
}
class Vehicle extends ClassWithId {
Vehicle(String firstIdSection, String lastIdSection) {
this.id = firstIdSection + ":vehicle:" + lastIdSection;
}
}
In this case the id field has two String values during building of the object, and the object knows how to construct the aggregated id.
Configuring what happens in the building step allows the separation of the builder interface and the value interface.
I've been banging my head for hours on this. Came up with this solution. Hope this helps somebody:
@Builder
@Getter
public class Person {
private String name;
private String address;
/**
* Uses custom builder class
*/
public static PersonBuilder builder() {
return new CustomPersonBuilder();
}
/**
* Cusotm builder class
*/
private static class CustomPersonBuilder extends PersonBuilder {
@Override
public Person build() {
// Validates required fields
Validate.notBlank(super.name, "Persons NAME cannot be null or empty!");
return super.build();
}
}
}
Also, if you don't want to create another builder.
@Value
@Builder(toBuilder = true)
@AllArgsConstructor
public static class User {
private String name;
public static UserBuilder builder() {
return new UserBuilder(){
@Override
public User build() {
prebuild();
return super.build();
}
};
}
public static class UserBuilder {
void prebuild(){
name="const";
}
}
}
__NOTE__ This is bad, can not debug the prebuild, prefer to use the hidden builder to extend the generated builder
We've thought about this a lot too and concluded it's just not nice enough to bother including.
You can always make a constructor or static 'factory' method that does your checks, and then slap a @Builder on that.
one more solution: .build().validate();
just add the validate() method that does the validation and returns 'this'
Like:
public MembershipValidationQuery validate() {
int age = LocalDate.now().getYear() - dateofBirth.getYear();
if (age > 120) {
log.warn("age {} is probably incorrect", age);
}
return this;
}
@Sergey80 but the issue with that is that you end up exposing an inconsistent interface to whoever will use your code. You're basically asking them to run .validate() at every step of the way, which sort of defeats the purpose of the builder to begin with.
I know. It is just I did not find another way. another way to create your own builder without Lombok (which I do not want).
none of these above helped me.
In my case, were is nobody but my app will use my code, validate() is a consistent interface, but depending on the problem it may not be so.
What I put is a workaround, not a solution.
Most helpful comment
I've been banging my head for hours on this. Came up with this solution. Hope this helps somebody: