FreeBuilder
FreeBuilder copied to clipboard
Feature request: hasX() methods
One issue I came across when looking into migrating to @FreeBuilder is that there is no (easy) way to test whether a field has been set on a builder. The builder class has a getFoo() method, but it returns an IllegalStateException if the field "foo" is not set. I could catch this exception and then set a value in the exception handler, but that's ugly. It would be much better to generate a method "hasFoo()" (or some better name) for a field called "foo" which returned true if the field had been set.
The normal way of setting a default value for a field, in the constructor, does not work in the case in which you only want to set the default value if it has not been set by the time build is called. The case I had was in testing, where I extended the builder in a test-only subclass which used a factory to create objects with sequentially-increasing IDs. If the factory creates an object, it increments the ID, so we don't want to set the field until and unless we get to build() and the field has not yet been set.
If you lazily set the field, won't you end up accidentally reusing IDs if the user reuses the Builder?
There are a couple of reasons why it's difficult to add hasX to @FreeBuilder in particular. First, there's no way to tell if a field has a default value assigned in the constructor, so you get hasX methods that are both pointless and potentially confusing. Secondly, customisation is done via overrides, so any extra method makes it harder to do.
In your specific case, I suggest adding a boolean hasX field and overriding setX and getX to respect it; this also lets you call super.setX in your build method without changing the state of hasX.
Essentially, what I am looking for is the ability to read the Set of unset fields from subclasses of the generated class. I do think it would be useful to have a way of determining "if I call build, will it work?" without resorting to catching exceptions. Even this may not be perfect if there is any validation done in an overridden build() method.
Set<Foo::Builder::Properties> unsetProperties()
would solve the "pointless methods" issue, but it's a big API to commit to. At the moment, we can switch to "null means unset" internally to trade off space (one less field, one less type) against readability; exposing the enum pins our implementation. Having a single method is definitely a good direction to explore in the design space, though.