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