1# AutoBuilder 2 3 4AutoBuilder makes it easy to create a generalized builder, with setter methods 5that accumulate values, and a build method that calls a constructor or static 6method with those values as parameters. Callers don't need to know the order of 7those parameters. Parameters can also have default values. There can be 8validation before the constructor or method call. 9 10If you are familiar with [AutoValue builders](builders.md) then AutoBuilder 11should also be familiar. Where an `@AutoValue.Builder` has setter methods 12corresponding to the getter methods in the `@AutoValue` class, an `@AutoBuilder` 13has setter methods corresponding to the parameters of a constructor or static 14method. Apart from that, the two are very similar. 15 16## Example: calling a constructor 17 18Here is a simple example: 19 20```java 21@AutoBuilder(ofClass = Person.class) 22abstract class PersonBuilder { 23 static PersonBuilder personBuilder() { 24 return new AutoBuilder_PersonBuilder(); 25 } 26 27 abstract PersonBuilder setName(String name); 28 abstract PersonBuilder setId(int id); 29 abstract Person build(); 30} 31``` 32 33It might be used like this: 34 35```java 36Person p = PersonBuilder.personBuilder().setName("Priz").setId(6).build(); 37``` 38 39That would have the same effect as this: 40 41```java 42Person p = new Person("Priz", 6); 43``` 44 45But it doesn't require you to know what order the constructor parameters are in. 46 47Here, `setName` and `setId` are _setter methods_. Calling 48`builder.setName("Priz")` records the value `"Priz"` for the parameter `name`, 49and likewise with `setId`. 50 51There is also a `build()` method. Calling that method invokes the `Person` 52constructor with the parameters that were previously set. 53 54## <a name="kotlin"></a> Example: calling a Kotlin constructor 55 56Kotlin has named arguments and default arguments for constructors and functions, 57which means there is not much need for anything like AutoBuilder there. But if 58you are constructing an instance of a Kotlin data class from Java code, 59AutoBuilder can help. 60 61Given this trivial Kotlin data class: 62 63```kotlin 64class KotlinData(val level: Int, val name: String?, val id: Long = -1L) 65``` 66 67You might make a builder for it like this: 68 69```java 70@AutoBuilder(ofClass = KotlinData.class) 71public abstract class KotlinDataBuilder { 72 public static KotlinDataBuilder kotlinDataBuilder() { 73 return new AutoBuilder_KotlinDataBuilder(); 74 } 75 76 public abstract KotlinDataBuilder setLevel(int x); 77 public abstract KotlinDataBuilder setName(@Nullable String x); 78 public abstract KotlinDataBuilder setId(long x); 79 public abstract KotlinData build(); 80} 81``` 82 83The Kotlin type `String?` corresponds to `@Nullable String` in the AutoBuilder 84class, where `@Nullable` is any annotation with that name, such as 85`org.jetbrains.annotations.Nullable`. 86 87The `id` parameter has a default value of `-1L`, which means that if `setId` is 88not called then the `id` field of the built `KotlinData` will be `-1L`. 89 90If you are using [kapt](https://kotlinlang.org/docs/kapt.html) then you can also 91define the builder in the data class itself: 92 93```kotlin 94class KotlinData(val level: Int, val name: String?, val id: Long = -1L) { 95 @AutoBuilder // we don't need ofClass: by default it is the containing class 96 interface Builder { 97 fun setLevel(x: Int): Builder 98 fun setName(x: String?): Builder 99 fun setId(x: Long): Builder 100 fun build(): KotlinData 101 } 102 103 fun toBuilder(): Builder = AutoBuilder_KotlinData_Builder(this) 104 105 companion object { 106 @JvmStatic fun builder(): Builder = AutoBuilder_KotlinData_Builder() 107 } 108} 109``` 110 111This example uses an interface rather than an abstract class for the builder, 112but both are possible. Java code would then construct instances like this: 113 114```java 115KotlinData k = KotlinData.builder().setLevel(23).build(); 116``` 117 118The example also implements a `toBuilder()` method to get a builder that starts 119out with values from the given instance. See [below](#to_builder) for more 120details on that. 121 122## The generated subclass 123 124Like `@AutoValue.Builder`, compiling an `@AutoBuilder` class will generate a 125concrete subclass. In the example above, this will be `class 126AutoBuilder_PersonBuilder extends PersonBuilder`. It is common to have a static 127`builder()` method, as in the example, which calls `new AutoBuilder_...()`. That 128will typically be the only reference to the generated class. 129 130If the `@AutoBuilder` type is nested then the name of the generated class 131reflects that nesting. For example: 132 133```java 134class Outer { 135 static class Inner { 136 @AutoBuilder 137 abstract static class Builder {...} 138 } 139 static Inner.Builder builder() { 140 return new AutoBuilder_Outer_Inner_Builder(); 141 } 142} 143``` 144 145## `@AutoBuilder` annotation parameters 146 147`@AutoBuilder` has two annotation parameters, `ofClass` and `callMethod`. 148 149If `ofClass` is specified, then `build()` will call a constructor or static 150method of that class. Otherwise it will call a constructor or static method of 151the class _containing_ the `@AutoBuilder` class. 152 153If `callMethod` is specified, then `build()` will call a static method with that 154name. Otherwise `build()` will call a constructor. 155 156The following examples illustrate the various possibilities. These examples use 157an interface for the `@AutoBuilder` type. You can also use an abstract class; if 158it is nested then it must be static. 159 160### Both `callMethod` and `ofClass` 161 162```java 163@AutoBuilder(callMethod = "of", ofClass = LocalTime.class) 164interface LocalTimeBuilder { 165 ... 166 LocalTime build(); // calls: LocalTime.of(...) 167} 168``` 169 170### Only `ofClass` 171 172```java 173@AutoBuilder(ofClass = Thread.class) 174interface ThreadBuilder { 175 ... 176 Thread build(); // calls: new Thread(...) 177} 178``` 179 180### Only `callMethod` 181 182```java 183class Foo { 184 static String concat(String first, String middle, String last) {...} 185 186 @AutoBuilder(callMethod = "concat") 187 interface ConcatBuilder { 188 ... 189 String build(); // calls: Foo.concat(first, middle, last) 190 } 191} 192``` 193 194Notice in this example that the static method returns `String`. The implicit 195`ofClass` is `Foo`, but the static method can return any type. 196 197### Neither `callMethod` nor `ofClass` 198 199```java 200class Person { 201 Person(String name, int id) {...} 202 203 @AutoBuilder 204 interface Builder { 205 ... 206 Person build(); // calls: new Person(name, id) 207 } 208} 209``` 210 211## The build method 212 213The build method must have a certain return type. If it calls a constructor then 214its return type must be the type of the constructed class. If it calls a static 215method then its return type must be the return type of the static method. 216 217The build method is often called `build()` but it does not have to be. The only 218requirement is that there must be exactly one no-arg abstract method that has 219the return type just described and that does not correspond to a parameter name. 220 221The following example uses the name `call()` since that more accurately reflects 222what it does: 223 224```java 225public class LogUtil { 226 public static void log(Level severity, String message, Object... params) {...} 227 228 @AutoBuilder(callMethod = "log") 229 public interface Caller { 230 Caller setSeverity(Level level); 231 Caller setMessage(String message); 232 Caller setParams(Object... params); 233 void call(); // calls: LogUtil.log(severity, message, params) 234 } 235``` 236 237## <a name="to_builder"></a> Making a builder from a built instance 238 239It is not always possible to map back from the result of a constructor or method 240call to a builder that might have produced it. But in one important case, it 241*is* possible. That's when every parameter in the constructor or method 242corresponds to a "getter method" in the built type. This will always be true 243when building a Java record or a Kotlin data class (provided its getters are 244visible to the builder). In this case, the generated builder class will have a 245second constructor that takes an object of the built type as a parameter and 246produces a builder that starts out with values from that object. That can then 247be used to produce a new object that may differ from the first one in just one 248or two properties. (This is very similar to AutoValue's 249[`toBuilder()`](builders-howto.md#to_builder) feature.) 250 251If the constructor or method has a parameter `String bar` then the built type 252must have a visible method `String bar()` or `String getBar()`. (Java records 253have the first and Kotlin data classes have the second.) If there is a 254similar corresponding method for every parameter then the second constructor is 255generated. 256 257If you are able to change the built type, the most convenient way to use this is 258to add a `toBuilder()` instance method that calls `new AutoBuilder_Foo(this)`. 259We saw this in the [Kotlin example](#kotlin) earlier. Otherwise, you can have 260a second static `builder` method, like this: 261 262```java 263@AutoBuilder(ofClass = Person.class) 264abstract class PersonBuilder { 265 static PersonBuilder personBuilder() { 266 return new AutoBuilder_PersonBuilder(); 267 } 268 static PersonBuilder personBuilder(Person person) { 269 return new AutoBuilder_PersonBuilder(person); 270 } 271 ... 272} 273``` 274 275## Overloaded constructors or methods 276 277There might be more than one constructor or static method that matches the 278`callMethod` and `ofClass`. AutoBuilder will ignore any that are not visible to 279the generated class, meaning private, or package-private and in a different 280package. Of the others, it will pick the one whose parameter names match the 281`@AutoBuilder` setter methods. It is a compilation error if there is not exactly 282one such method or constructor. 283 284## Generics 285 286If the builder calls the constructor of a generic type, then it must have the 287same type parameters as that type, as in this example: 288 289```java 290class NumberPair<T extends Number> { 291 NumberPair(T first, T second) {...} 292 293 @AutoBuilder 294 interface Builder<T extends Number> { 295 Builder<T> setFirst(T x); 296 Builder<T> setSecond(T x); 297 NumberPair<T> build(); 298 } 299} 300``` 301 302If the builder calls a static method with type parameters, then it must have the 303same type parameters, as in this example: 304 305```java 306class Utils { 307 static <K extends Number, V> Map<K, V> singletonNumberMap(K key, V value) {...} 308 309 @AutoBuilder(callMethod = "singletonNumberMap") 310 interface Builder<K extends Number, V> { 311 Builder<K, V> setKey(K x); 312 Builder<K, V> setValue(V x); 313 Map<K, V> build(); 314 } 315} 316``` 317 318Although it's unusual, a Java constructor can have its own type parameters, 319separately from any that its containing class might have. A builder that calls a 320constructor like that must have the type parameters of the class followed by the 321type parameters of the constructor: 322 323```java 324class CheckedSet<E> implements Set<E> { 325 <T extends E> CheckedSet(Class<T> type) {...} 326 327 @AutoBuilder 328 interface Builder<E, T extends E> { 329 Builder<E, T> setType(Class<T> type); 330 CheckedSet<E> build(); 331 } 332} 333``` 334 335## Required, optional, and nullable parameters 336 337Parameters that are annotated `@Nullable` are null by default. Parameters of 338type `Optional`, `OptionalInt`, `OptionalLong`, and `OptionalDouble` are empty 339by default. Kotlin constructor parameters with default values get those values 340by default. Every other parameter is _required_, meaning that the build method 341will throw `IllegalStateException` if any are omitted. 342 343To establish default values for parameters, set them in the `builder()` method 344before returning the builder. 345 346```java 347class Foo { 348 Foo(String bar, @Nullable String baz, String buh) {...} 349 350 static Builder builder() { 351 return new AutoBuilder_Foo_Builder() 352 .setBar(DEFAULT_BAR); 353 } 354 355 @AutoBuilder 356 interface Builder { 357 Builder setBar(String x); 358 Builder setBaz(String x); 359 Builder setBuh(String x); 360 Foo build(); 361 } 362 363 { 364 builder().build(); // IllegalStateException, buh is not set 365 builder().setBuh("buh").build(); // OK, bar=DEFAULT_BAR and baz=null 366 builder().setBaz(null).setBuh("buh").build(); // OK 367 builder().setBar(null); // NullPointerException, bar is not @Nullable 368 } 369} 370``` 371 372Trying to set a parameter that is _not_ annotated `@Nullable` to `null` will 373produce a `NullPointerException`. 374 375`@Nullable` here is any annotation with that name, such as 376`javax.annotation.Nullable` or 377`org.checkerframework.checker.nullness.qual.Nullable`. 378 379## Getters 380 381The `@AutoBuilder` class or interface can also have _getter_ methods. A getter 382method returns the value that has been set for a certain parameter. Its return 383type can be either the same as the parameter type, or an `Optional` wrapping 384that type. Calling the getter before any value has been set will throw an 385exception in the first case or return an empty `Optional` in the second. 386 387In this example, the `nickname` parameter defaults to the same value as the 388`name` parameter but can also be set to a different value: 389 390```java 391public class Named { 392 Named(String name, String nickname) {...} 393 394 @AutoBuilder 395 public abstract static class Builder { 396 public abstract Builder setName(String x); 397 public abstract Builder setNickname(String x); 398 abstract String getName(); 399 abstract Optional<String> getNickname(); 400 abstract Named autoBuild(); 401 402 public Named build() { 403 if (!getNickname().isPresent()) { 404 setNickname(getName()); 405 } 406 return autoBuild(); 407 } 408 } 409} 410``` 411 412The example illustrates having a package-private `autoBuild()` method that 413AutoBuilder implements. The public `build()` method calls it after adjusting the 414nickname if necessary. 415 416The builder in the example is an abstract class rather than an interface. An 417abstract class allows us to distinguish between public methods for users of the 418builder to call, and package-private methods that the builder's own logic uses. 419 420## Building annotation instances 421 422AutoBuilder can build instances of annotation interfaces. When the annotation 423has no elements (methods in the annotation), or only one, then AutoAnnotation is 424simpler to use. But when there are several elements, a builder is helpful. See 425[here](howto.md#annotation) for examples of both. 426 427## Naming conventions 428 429A setter method for the parameter `foo` can be called either `setFoo` or `foo`. 430A getter method can be called either `getFoo` or `foo`, and for a `boolean` 431parameter it can also be called `isFoo`. The choice for getters and setters is 432independent. For example your getter might be `foo()` while your setter is 433`setFoo(T)`. 434 435By convention, the parameter name of a setter method either echoes the parameter 436being set:<br> 437`Builder setName(String name);`<br> 438or it is just `x`:<br> 439`Builder setName(String x);`<br> 440 441If class `Foo` has a nested `@AutoBuilder` that builds instances of `Foo`, then 442conventionally that type is called `Builder`, and instances of it are obtained 443by calling a static `Foo.builder()` method: 444 445```java 446Foo foo1 = Foo.builder().setBar(bar).setBaz(baz).build(); 447Foo.Builder fooBuilder = Foo.builder(); 448``` 449 450If an `@AutoBuilder` for `Foo` is its own top-level class then that class will 451typically be called `FooBuilder` and it will have a static `fooBuilder()` method 452that returns an instance of `FooBuilder`. That way callers can statically import 453`FooBuilder.fooBuilder` and just write `fooBuilder()` in their code. 454 455```java 456@AutoBuilder(ofClass = Foo.class) 457public abstract class FooBuilder { 458 public static FooBuilder fooBuilder() { 459 return new AutoBuilder_FooBuilder(); 460 } 461 ... 462 public abstract Foo build(); 463} 464``` 465 466If an `@AutoBuilder` is designed to call a static method that is not a factory 467method, the word "call" is better than "build" in the name of the type 468(`FooCaller`), the static method (`fooCaller()`), and the "build" method (`call()`). 469 470```java 471@AutoBuilder(callMethod = "log", ofClass = MyLogger.class) 472public abstract class LogCaller { 473 public static LogCaller logCaller() { 474 return new AutoBuilder_LogCaller(); 475 } 476 ... 477 public abstract void call(); 478} 479 480// used as: 481logCaller().setLevel(Level.INFO).setMessage("oops").call(); 482``` 483 484## Other builder features 485 486There are a number of other builder features that have not been detailed here 487because they are the same as for `@AutoValue.Builder`. They include: 488 489* [Special treatment of collections](builders-howto.md#collection) 490* [Handling of nested builders](builders-howto.md#nested_builders) 491 492## When parameter names are unavailable 493 494AutoBuilder depends on knowing the names of parameters. But parameter names are 495not always available in Java. They _are_ available in these cases, at least: 496 497* In code that is being compiled at the same time as the `@AutoBuilder` class 498 or interface. 499* In _records_ (from Java 16 onwards). 500* In the constructors of Kotlin data classes. 501* In code that was compiled with the [`-parameters`] option. 502 503A Java compiler bug means that parameter names are not available to AutoBuilder 504when compiling with JDK versions before 11, in any of these cases except the 505first. We recommend building with a recent JDK, using the `--release` option if 506necessary to produce code that can run on earlier versions. 507 508If parameter names are unavailable, you always have the option of introducing a 509static method in the same class as the `@AutoBuilder` type, and having it call 510the method you want. Since it is compiled at the same time, its parameter names 511are available. 512 513Here's an example of fixing a problem this way. The code here typically will not 514compile, since parameter names of JDK methods are not available: 515 516```java 517import java.time.LocalTime; 518 519public class TimeUtils { 520 // Does not work, since parameter names from LocalTime.of are unavailable. 521 @AutoBuilder(callMethod = "of", ofClass = LocalTime.class) 522 public interface TimeBuilder { 523 TimeBuilder setHour(int x); 524 TimeBuilder setMinute(int x); 525 TimeBuilder setSecond(int x); 526 LocalTime build(); 527 } 528} 529``` 530 531It will produce an error message like this: 532 533``` 534error: [AutoBuilderNoMatch] Property names do not correspond to the parameter names of any static method named "of": 535 public interface TimeBuilder { 536 ^ 537 of(int arg0, int arg1) 538 of(int arg0, int arg1, int arg2) 539 of(int arg0, int arg1, int arg2, int arg3) 540``` 541 542The names `arg0`, `arg1`, etc are concocted by the compiler because it doesn't 543have the real names. 544 545Introducing a static method fixes the problem: 546 547```java 548import java.time.LocalTime; 549 550public class TimeUtils { 551 static LocalTime localTimeOf(int hour, int minute, int second) { 552 return LocalTime.of(hour, minute, second); 553 } 554 555 @AutoBuilder(callMethod = "localTimeOf") 556 public interface TimeBuilder { 557 TimeBuilder setHour(int x); 558 TimeBuilder setMinute(int x); 559 TimeBuilder setSecond(int x); 560 LocalTime build(); 561 } 562} 563``` 564 565[`-parameters`]: https://docs.oracle.com/en/java/javase/16/docs/specs/man/javac.html#option-parameters 566