AtBuilder generates safe builders for your records with support for nullable and omittable types.
// com/example/Person.java
package com.example;
import com.osmerion.atbuilder.Builder;
@Builder
public record Person(
String name,
@Nullable String firstName,
Omitable<String> nickname,
Omittable<@Nullable String> country,
String employmentStatus
) {
public static PersonBuilder builder() {
return new PersonBuilder()
.employmentStatus("EMPLOYED");
}
}
// Usage
Person person = Person.builder()
.name("John Doe")
.build(); // IllegalStateException: Missing required 'firstName' component
Person person = Person.builder()
.name("John Doe")
.firstName(null)
.build(); // Success!
Person person = Person.builder()
.name("John Doe")
.nickname(null) // NullPointerException: 'nickname' may not be null
.build();
Person person = Person.builder()
.name("John Doe")
.country(null)
.build(); // Success!AtBuilder ships in two artifacts:
- The
com.osmerion.atbuilder:atbuilder-runtimeartifact which contains the@Builderannotation and the runtime support library for the generated builders. - The
com.osmerion.atbuilder:atbuilder-processorartifact which contains the annotation processor that is responsible for generating the builders.
Setting up AtBuilder in Gradle
When using Gradle, the dependencies can simply be added to the respective configurations:
dependencies {
implementation("com.osmerion.atbuilder:atbuilder-runtime:<version>")
annotationProcessor("com.osmerion.atbuilder:atbuilder-processor:<version>")
}Setting up AtBuilder in Maven
When using Maven, the maven-compiler-plugin needs to be configured to use the annotation processor:
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${version}</version>
<configuration>
<annotationProcessorPaths>
<annotationProcessorPath>
<groupId>com.osmerion</groupId>
<artifactId>atbuilder-processor</artifactId>
<version>${version}</version>
</annotationProcessorPath>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<dependencies>
<dependency>
<groupId>com.osmerion.atbuilder</groupId>
<artifactId>atbuilder-runtime</artifactId>
<version>${version}</version>
</dependency>
</dependencies>To use AtBuilder, simply annotate your record with the @Builder annotation.
The annotation processor will then generate a builder class for the record.
The builder is generated with a package-private constructor. It is recommended
to provide a static builder method in the annotated record that delegates to
the constructor. This method may be used to prepopulate the builder instance with
values that serve as default values.
import com.osmerion.atbuilder.Builder;
@Builder
public record Person(
String name,
String country
) {
public static PersonBuilder builder() {
return new PersonBuilder()
.country("Germany");
}
}An instance of the annotated record can be created by calling the build method
of a builder instance. This method checks if values for all required components
have been set and throws an IllegalStateException otherwise. Omittable
components are not required and initialized with Omittable.absent() when no
value is set.
// com/example/Person.java
import com.osmerion.atbuilder.Builder;
@Builder
public record Person(
String name,
Omittable<Integer> age
) {
public static PersonBuilder builder() {
return new PersonBuilder();
}
}
// Usage
Person person = Person.builder()
.build(); // IllegalStateException: Missing required 'name' component
Person person = Person.builder()
.name("Alice")
.build(); // Success! "age" is initialized with Omittable.absent()Applicable annotations from record components are copied to the generated
builder. Notably, this preserves nullability information carried by annotations
(such as Nullable). Additionally, applicable @NullMarked and @NullUnmarked
annotations are copied to the generated builder class.
// com/example/Person.java
package com.example;
import com.osmerion.atbuilder.Builder;
import org.jspecify.annotations.Nullable;
import org.jspecify.annotations.NullMarked;
@Builder
@NullMarked
public record Person(String name, @Nullable String nickname) {}
// com/example/PersonBuilder.java (generated)
package com.example;
import org.jspecify.annotations.Nullable;
import org.jspecify.annotations.NullMarked;
@NullMarked
public class PersonBuilder {
...
public PersonBuilder nickname(@Nullable String nickname) { ... }
}This project uses Gradle's toolchain support to detect and select the JDKs required to run the build. Please refer to the build scripts to find out which toolchains are requested.
An installed JDK 17 (or later) is required to use Gradle.
Once the setup is complete, invoke the respective Gradle tasks using the following command on Unix/macOS:
./gradlew <tasks>
or the following command on Windows:
gradlew <tasks>
Important Gradle tasks to remember are:
clean- clean build resultsbuild- assemble and test the projectpublishToMavenLocal- build and install all public artifacts to the local maven repository
Additionally tasks may be used to print a list of all available tasks.
Copyright 2025 Leon Linhart
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.