CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-google-auto-value--auto-value

Generated immutable value classes for Java 8+ using annotation processing

Pending
Overview
Eval results
Files

standalone-builders.mddocs/

Standalone Builders

AutoBuilder generates builders for existing classes or constructors, allowing you to add fluent APIs to classes that weren't originally designed with builders.

Basic Standalone Builder

// Existing class that you want to build
public class Person {
  private final String name;
  private final int age;
  private final String email;
  
  public Person(String name, int age, String email) {
    this.name = name;
    this.age = age;
    this.email = email;
  }
  
  // getters...
}

// Generate a builder for the existing class
@AutoBuilder(ofClass = Person.class)
public abstract class PersonBuilder {
  public static PersonBuilder builder() {
    return new AutoBuilder_PersonBuilder();
  }
  
  public abstract PersonBuilder setName(String name);
  public abstract PersonBuilder setAge(int age);
  public abstract PersonBuilder setEmail(String email);
  public abstract Person build();
}

Usage Example

Person person = PersonBuilder.builder()
    .setName("Alice")
    .setAge(30)
    .setEmail("alice@example.com")
    .build();

Builder for Static Factory Method

Build objects via static factory methods instead of constructors:

// Existing class with static factory
public class DatabaseConnection {
  private final String host;
  private final int port;
  private final String database;
  
  private DatabaseConnection(String host, int port, String database) {
    this.host = host;
    this.port = port;
    this.database = database;
  }
  
  public static DatabaseConnection connect(String host, int port, String database) {
    return new DatabaseConnection(host, port, database);
  }
}

// Builder targeting the static method
@AutoBuilder(ofClass = DatabaseConnection.class, callMethod = "connect")
public abstract class DatabaseConnectionBuilder {
  public static DatabaseConnectionBuilder builder() {
    return new AutoBuilder_DatabaseConnectionBuilder();
  }
  
  public abstract DatabaseConnectionBuilder setHost(String host);
  public abstract DatabaseConnectionBuilder setPort(int port);
  public abstract DatabaseConnectionBuilder setDatabase(String database);
  public abstract DatabaseConnection build();
}

Usage:

DatabaseConnection conn = DatabaseConnectionBuilder.builder()
    .setHost("localhost")
    .setPort(5432)
    .setDatabase("myapp")
    .build();

Builder with Default Values

Set default values in the builder factory:

@AutoBuilder(ofClass = HttpConfig.class)
public abstract class HttpConfigBuilder {
  public static HttpConfigBuilder builder() {
    return new AutoBuilder_HttpConfigBuilder()
        .setTimeout(5000)
        .setRetries(3)
        .setFollowRedirects(true);
  }
  
  public abstract HttpConfigBuilder setUrl(String url);
  public abstract HttpConfigBuilder setTimeout(int timeout);
  public abstract HttpConfigBuilder setRetries(int retries);
  public abstract HttpConfigBuilder setFollowRedirects(boolean followRedirects);
  public abstract HttpConfig build();
}

Builder for Generic Classes

AutoBuilder supports generic types:

// Generic class to build
public class Pair<T, U> {
  private final T first;
  private final U second;
  
  public Pair(T first, U second) {
    this.first = first;
    this.second = second;
  }
  
  // getters...
}

// Generic builder
@AutoBuilder(ofClass = Pair.class)
public abstract class PairBuilder<T, U> {
  public static <T, U> PairBuilder<T, U> builder() {
    return new AutoBuilder_PairBuilder<>();
  }
  
  public abstract PairBuilder<T, U> setFirst(T first);
  public abstract PairBuilder<T, U> setSecond(U second);
  public abstract Pair<T, U> build();
}

Usage:

Pair<String, Integer> pair = PairBuilder.<String, Integer>builder()
    .setFirst("hello")
    .setSecond(42)
    .build();

Builder for Classes with Multiple Constructors

AutoBuilder will match the constructor based on parameter names and types:

// Class with multiple constructors
public class Employee {
  private final String name;
  private final String department;
  private final int salary;
  private final String email;
  
  // Constructor 1
  public Employee(String name, String department) {
    this(name, department, 0, null);
  }
  
  // Constructor 2  
  public Employee(String name, String department, int salary, String email) {
    this.name = name;
    this.department = department;
    this.salary = salary;
    this.email = email;
  }
}

// Builder matches the 4-parameter constructor
@AutoBuilder(ofClass = Employee.class)
public abstract class EmployeeBuilder {
  public static EmployeeBuilder builder() {
    return new AutoBuilder_EmployeeBuilder();
  }
  
  public abstract EmployeeBuilder setName(String name);
  public abstract EmployeeBuilder setDepartment(String department);
  public abstract EmployeeBuilder setSalary(int salary);
  public abstract EmployeeBuilder setEmail(String email);
  public abstract Employee build();
}

Builder for Record Classes (Java 14+)

AutoBuilder can generate builders for record classes:

// Record class
public record Point(double x, double y, String label) {}

// Builder for the record
@AutoBuilder(ofClass = Point.class)
public abstract class PointBuilder {
  public static PointBuilder builder() {
    return new AutoBuilder_PointBuilder();
  }
  
  public abstract PointBuilder setX(double x);
  public abstract PointBuilder setY(double y);
  public abstract PointBuilder setLabel(String label);
  public abstract Point build();
}

Usage:

Point origin = PointBuilder.builder()
    .setX(0.0)
    .setY(0.0)
    .setLabel("Origin")
    .build();

Builder with Validation

Add validation to standalone builders:

@AutoBuilder(ofClass = Account.class)
public abstract class AccountBuilder {
  public static AccountBuilder builder() {
    return new AutoBuilder_AccountBuilder();
  }
  
  public abstract AccountBuilder setUsername(String username);
  public abstract AccountBuilder setEmail(String email);
  public abstract AccountBuilder setAge(int age);
  
  abstract Account autoBuild(); // Package-private
  
  public Account build() {
    Account account = autoBuild();
    validate(account);
    return account;
  }
  
  private void validate(Account account) {
    if (account.getUsername().length() < 3) {
      throw new IllegalArgumentException("Username must be at least 3 characters");
    }
    if (!account.getEmail().contains("@")) {
      throw new IllegalArgumentException("Invalid email format");
    }
    if (account.getAge() < 0) {
      throw new IllegalArgumentException("Age must be non-negative"); 
    }
  }
}

Builder for Immutable Collections

Use AutoBuilder with collection factory methods:

// Building ImmutableList
@AutoBuilder(ofClass = ImmutableList.class, callMethod = "of")
public abstract class ImmutableListBuilder<T> {
  public static <T> ImmutableListBuilder<T> builder() {
    return new AutoBuilder_ImmutableListBuilder<>();
  }
  
  // For ImmutableList.of(T... elements)
  public abstract ImmutableListBuilder<T> setElements(T... elements);
  public abstract ImmutableList<T> build();
}

Usage:

ImmutableList<String> list = ImmutableListBuilder.<String>builder()
    .setElements("a", "b", "c")
    .build();

Builder for Annotation Creation

Combine with @AutoAnnotation for fluent annotation building:

// Annotation to build
public @interface ApiEndpoint {
  String path();
  String method() default "GET";
  boolean authenticated() default true;
}

// AutoAnnotation factory
public class Annotations {
  @AutoAnnotation
  public static ApiEndpoint apiEndpoint(String path, String method, boolean authenticated) {
    return new AutoAnnotation_Annotations_apiEndpoint(path, method, authenticated);
  }
}

// Builder for the annotation
@AutoBuilder(callMethod = "apiEndpoint", ofClass = Annotations.class)
public abstract class ApiEndpointBuilder {
  public static ApiEndpointBuilder builder() {
    return new AutoBuilder_ApiEndpointBuilder()
        .setMethod("GET")
        .setAuthenticated(true);
  }
  
  public abstract ApiEndpointBuilder setPath(String path);
  public abstract ApiEndpointBuilder setMethod(String method);
  public abstract ApiEndpointBuilder setAuthenticated(boolean authenticated);
  public abstract ApiEndpoint build();
}

Usage:

ApiEndpoint endpoint = ApiEndpointBuilder.builder()
    .setPath("/api/users")
    .setMethod("POST")
    .build();

Nested Class Builders

AutoBuilder works with nested classes:

public class OuterClass {
  public static class InnerClass {
    private final String value;
    
    public InnerClass(String value) {
      this.value = value;
    }
  }
}

@AutoBuilder(ofClass = OuterClass.InnerClass.class)
public abstract class InnerClassBuilder {
  public static InnerClassBuilder builder() {
    return new AutoBuilder_InnerClassBuilder();
  }
  
  public abstract InnerClassBuilder setValue(String value);
  public abstract OuterClass.InnerClass build();
}

Error Handling

AutoBuilder provides clear error messages for common issues:

// Compilation error if parameter names don't match constructor
@AutoBuilder(ofClass = Person.class)
public abstract class BadPersonBuilder {
  public abstract BadPersonBuilder setFullName(String fullName); // ERROR: no parameter named 'fullName'
  public abstract Person build();
}

// Runtime validation in build methods
Account account = AccountBuilder.builder()
    .setUsername("ab") // Will throw IllegalArgumentException during build()
    .build();

Install with Tessl CLI

npx tessl i tessl/maven-com-google-auto-value--auto-value

docs

annotation-generation.md

builders.md

extensions.md

index.md

memoization.md

pretty-strings.md

serialization.md

standalone-builders.md

tagged-unions.md

value-classes.md

tile.json