@Builder tricks for default values and mandatory attributes

2,777 views
Skip to first unread message

pcouton

unread,
Jun 8, 2015, 11:01:27 AM6/8/15
to project...@googlegroups.com
Hi all,

I realized today that It is possible to create its own builder, derived from the one generated by Lombok, as a work-around to provide default values to the builder:

@Getter
@Builder(builderClassName = "LombokBuilder")
public class Point {
   
private final double x;
   
private final double y;
   
private final double z;
   
private String name;

   
public static MyPointBuilder builder() {
       
return new MyPointBuilder();
   
}

   
public static class MyPointBuilder extends LombokBuilder {

       
public MyPointBuilder() {
            z
(-99.9); // Provide a default value for the z attribute
       
}

   
}
}

This works pretty well !

An idea is to use this mechanism to check that mandatory attributes are initialized by overriding the build() method. In my example, this would be done by adding this to MyPointBuilder:

    public static class MyPointBuilder extends LombokBuilder {
        ...

       
@Override
       
public Point build() {
           
if (name == null) throw new IllegalArgumentException("name attribute is mandatory");
            return super.build();
       
}
        ...
   
}

However, this not currently possible since builder attributes are private :-( Maybe it would be a good idea to make them protected in a future version ?

Any opinion on  this way of proceeding ?

Philippe

Martin Grajcar

unread,
Jun 8, 2015, 12:08:46 PM6/8/15
to project...@googlegroups.com
However, this not currently possible since builder attributes are private :-( Maybe it would be a good idea to make them protected in a future version ?

Any opinion on  this way of proceeding ?

Creating a new class only for this check sounds wrong. You problem with name==null can be solved trivially by simply annotating name with @Nonnull, and the the ctor will throw. For other problems, there's currently no goo solution. You could provide your own build() method with all needed checks and the constructor call. This is surely cleaner than extending the class and usually even shorter.

A @Getter would help, too, unfortunately many annotations are prohibited on builder, vote for issue 696.

pcouton

unread,
Jun 9, 2015, 5:29:30 AM6/9/15
to project...@googlegroups.com

... You problem with name==null can be solved trivially by simply annotating name with @Nonnull, and the the ctor will throw. 

 
I do not agree with you to rely on the use of @Nonnull for two reasons:
  • As a library writer, throwing a NPE to signal that a mandatory attribute has not been initialized in the builder would be a source of misunderstanding for the library users ;
  • This does not work for primitive attributes.
However, for having this last point working, it requires to initialize the builder's attribute with a default value (e.g. initialize a double attribute with Double.NaN when 0.0 is a legal value). And currently, one way is to derive a custom builder...

Alex K

unread,
Jun 9, 2015, 5:29:31 PM6/9/15
to project...@googlegroups.com
Isn't that a responsibility of a constructor to guarantee that the object is being constructed in a valid state?

Why Builder should do this? Isn't its purpose to allow assembling objects from multiple combinations of parameters and ultimately call the all-args constructor which should do the validation, fill in defaults etc?

(Also, @NonNull allows to specify exception type)

pcouton

unread,
Jun 10, 2015, 12:38:55 PM6/10/15
to project...@googlegroups.com

(Also, @NonNull allows to specify exception type)

I missed that, thanks for the hint!

Otherwise that, I agree with you for the roles of each part. However, could you explain how the all-args constructor could fill in defaults if all its args are given by the builder, which has no defaults itself?
Reply all
Reply to author
Forward
0 new messages