Skip to content

Latest commit

 

History

History
382 lines (296 loc) · 11.3 KB

builders-howto.md

File metadata and controls

382 lines (296 loc) · 11.3 KB

How do I... (Builder edition)

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.

Contents

How do I...

... use (or not use) set prefixes?

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 different names besides builder()/Builder/build()?

Use whichever names you like; AutoValue doesn't actually care.

(We would gently recommend these names as conventional.)

... specify a default value for a property?

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.

... initialize a builder to the same property values as an existing value instance

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;

... validate property values?

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;
}