This page answers common how-to questions that may come up when using AutoValue with the builder option. You should read and understand AutoValue with builders first.
If you are not using a builder, see Introduction and How do I... instead.
How do I...
- ... use (or not use)
set
prefixes? - ... use different names besides
builder()
/Builder
/build()
? - ... specify a default value for a property?
- ... initialize a builder to the same property values as an existing value instance
- ... validate property values?
Just as you can choose whether to use JavaBeans-style names for property getters
(getFoo()
or just foo()
) in your value class, you have the same choice for
setters in builders too (setFoo(value)
or just foo(value)
). As with getters,
you must use these prefixes consistently or not at all.
Using get
/is
prefixes for getters and using the set
prefix for setters are
independent choices. For example, it is fine to use the set
prefixes on all
your builder methods, but omit the get
/is
prefixes from all your accessors.
Here is the Animal
example using get
prefixes but not set
prefixes:
/**
* @AutoValue
*/
abstract class Animal
{
abstract function getName(): string;
abstract function getNumberOfLegs(): int;
static function builder(): AnimalBuilder
{
return new AutoValue_AnimalBuilder();
}
}
/**
* @AutoValue\Builder
*/
abstract class AnimalBuilder
{
abstract function name(string $value): self;
abstract function numberOfLegs(int $value): self;
abstract function build(): Animal;
}
Use whichever names you like; AutoValue doesn't actually care.
(We would gently recommend these names as conventional.)
What should happen when a caller does not supply a value for a property before
calling build()
? If the property in question is nullable,
it will simply default to null
as you would expect. But if is not nullable,
then build()
will throw an exception.
But this presents a problem, since one of the main advantages of a builder in the first place is that callers can specify only the properties they care about!
The solution is to provide a default value for such properties. Fortunately this
is easy: just set it on the newly-constructed builder instance before returning
it from the builder()
method.
Here is the Animal
example with the default number of legs being 4:
/**
* @AutoValue
*/
abstract class Animal
{
abstract function name(): string;
abstract function numberOfLegs(): int;
static function builder(): AnimalBuilder
{
return new AutoValue_AnimalBuilder()->setNumberOfLegs(4);
}
}
Occasionally you may want to supply a default value, but only if the property is not set explicitly. This is covered in the section on normalization.
Suppose your caller has an existing instance of your value class, and wants to
change only one or two of its properties. Of course, it's immutable, but it
would be convenient if they could easily get a Builder
instance representing
the same property values, which they could then modify and use to create a new
value instance.
To give them this ability, just add an abstract toBuilder
method, returning
your abstract builder type, to your value class. AutoValue will implement it.
abstract function toBuilder(): FooBuilder;
Validating properties is a little less straightforward than it is in the non-builder case.
What you need to do is split your "build" method into two methods:
- the non-visible, abstract method that AutoValue implements
- and the visible, concrete method you provide, which calls the generated method and performs validation.
We recommend naming these methods autoBuild
and build
, but any names will
work. It ends up looking like this:
/**
* @AutoValue\Builder
*/
abstract class AnimalBuilder {
abstract function name(string $value): self;
abstract function numberOfLegs(int $value): self;
function build(): Animal
{
$animal = $this->autoBuild();
assert($animal->numberOfLegs() >= 0, "Negative legs");
return $animal;
}
protected abstract function autoBuild(): Animal;
}