Generated immutable value classes for Java 8+ using annotation processing
—
AutoBuilder generates builders for existing classes or constructors, allowing you to add fluent APIs to classes that weren't originally designed with builders.
// 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();
}Person person = PersonBuilder.builder()
.setName("Alice")
.setAge(30)
.setEmail("alice@example.com")
.build();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();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();
}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();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();
}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();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");
}
}
}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();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();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();
}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