Compile-time annotation processor for generating immutable value objects with builder patterns and compile-time validation.
—
Comprehensive styling system for customizing naming conventions, generation behavior, validation methods, and code structure. The @Value.Style annotation provides extensive configuration options for tailoring generated code to specific project requirements.
Central configuration annotation for customizing all aspects of code generation.
/**
* Naming and structural style configuration for generated immutable
* implementations and companion classes. Can be placed on class, package,
* or used as meta-annotation.
*/
@interface Value.Style {
// Naming templates for method recognition and generation
String[] get() default "get*";
String init() default "*";
String with() default "with*";
String withUnaryOperator() default "";
String add() default "add*";
String addAll() default "addAll*";
String put() default "put*";
String putAll() default "putAll*";
String copyOf() default "copyOf";
String of() default "of";
String instance() default "of";
String builder() default "builder";
String newBuilder() default "new";
String from() default "from";
String build() default "build";
String buildOrThrow() default "";
String canBuild() default "";
String toBuilder() default "";
String set() default "set*";
String unset() default "unset*";
String clear() default "clear";
String create() default "create";
String toImmutable() default "toImmutable";
String isInitialized() default "isInitialized";
String isSet() default "*IsSet";
// Type naming templates
String typeImmutable() default "Immutable*";
String typeBuilder() default "Builder";
String typeInnerBuilder() default "Builder";
String[] typeAbstract() default "Abstract*";
String typeImmutableEnclosing() default "Immutable*";
String typeImmutableNested() default "*";
String typeModifiable() default "Modifiable*";
String typeInnerModifiable() default "Modifiable";
String typeWith() default "With*";
String packageGenerated() default "*";
// Behavioral configuration flags
boolean strictBuilder() default false;
boolean strictModifiable() default true;
boolean allParameters() default false;
boolean jdkOnly() default false;
boolean jdk9Collections() default false;
ValidationMethod validationMethod() default ValidationMethod.SIMPLE;
ImplementationVisibility visibility() default ImplementationVisibility.SAME;
BuilderVisibility builderVisibility() default BuilderVisibility.PUBLIC;
String visibilityString() default "";
String builderVisibilityString() default "";
// Advanced configuration options
boolean depluralize() default false;
String[] depluralizeDictionary() default {};
boolean deepImmutablesDetection() default false;
boolean stagedBuilder() default false;
boolean optionalAcceptNullable() default false;
boolean generateSuppressAllWarnings() default true;
boolean privateNoargConstructor() default false;
boolean protectedNoargConstructor() default false;
boolean attributelessSingleton() default false;
boolean unsafeDefaultAndDerived() default false;
boolean clearBuilder() default false;
boolean deferCollectionAllocation() default false;
boolean overshadowImplementation() default false;
boolean implementationNestedInBuilder() default false;
boolean forceJacksonPropertyNames() default true;
boolean forceJacksonIgnoreFields() default false;
boolean forceEqualsInWithers() default false;
boolean jacksonIntegration() default true;
boolean weakInterning() default false;
boolean alwaysPublicInitializers() default true;
boolean builtinContainerAttributes() default true;
boolean beanFriendlyModifiables() default false;
boolean allMandatoryParameters() default false;
boolean transientDerivedFields() default true;
boolean finalInstanceFields() default true;
boolean attributeBuilderDetection() default false;
String redactedMask() default "";
String nullableAnnotation() default "Nullable";
String[] attributeBuilder() default {"Builder", "*Builder", "builder", "from", "build", "*Build", "new"};
String getBuilder() default "*Builder";
String setBuilder() default "*Builder";
String addBuilder() default "add*Builder";
String addAllBuilder() default "addAll*Builders";
String getBuilders() default "*Builders";
Class<? extends RuntimeException> throwForInvalidImmutableState() default IllegalStateException.class;
Class<? extends RuntimeException> throwForNullPointer() default NullPointerException.class;
Class<? extends Annotation>[] passAnnotations() default {};
Class<? extends Annotation>[] additionalJsonAnnotations() default {};
Class<?>[] immutableCopyOfRoutines() default {};
Class<? extends Annotation>[] allowedClasspathAnnotations() default {};
Class<? extends Annotation> fallbackNullableAnnotation() default Inherited.class;
int limitStringLengthInToString() default 0;
boolean headerComments() default false;
boolean defaultAsDefault() default false;
boolean jakarta() default false;
}Control method and type naming conventions throughout generated code.
Usage Examples:
// Custom naming style
@Value.Style(
typeImmutable = "*Impl", // Generate PersonImpl instead of ImmutablePerson
typeBuilder = "*Builder", // Generate PersonBuilder
get = {"get*", "is*"}, // Recognize both get* and is* prefixes
with = "set*", // Use set* instead of with* for copy methods
init = "set*" // Use set* for builder methods
)
@Value.Immutable
public interface Person {
String getName();
boolean isActive();
}
// Generated class: PersonImpl with PersonBuilder
PersonImpl person = PersonImpl.builder()
.setName("John")
.setActive(true)
.build();
PersonImpl updated = person.setName("Jane"); // Copy method
// Bean-style naming
@Value.Style(
get = {"get*", "is*"},
init = "set*",
with = "set*",
typeImmutable = "*Bean"
)
@Value.Immutable
public interface UserBean {
String getName();
boolean isEnabled();
}
// Usage follows JavaBean conventions
UserBeanBean user = UserBeanBean.builder()
.setName("Alice")
.setEnabled(true)
.build();Control builder generation and behavior patterns.
/**
* Builder behavior configuration options
*/
boolean strictBuilder() default false; // Forward-only builders
boolean stagedBuilder() default false; // Telescopic/staged builders
boolean clearBuilder() default false; // Generate clear() method
String canBuild() default ""; // Generate canBuild() method
String buildOrThrow() default ""; // Generate buildOrThrow() methodUsage Examples:
// Strict builders - forward-only, no reset
@Value.Style(strictBuilder = true)
@Value.Immutable
public interface Config {
String name();
int value();
List<String> tags();
}
// Strict builder prevents re-initialization
ImmutableConfig.Builder builder = ImmutableConfig.builder();
builder.name("test");
// builder.name("other"); // Would throw exception
builder.addTags("tag1", "tag2");
// builder.tags(List.of("different")); // No reset method generated
// Staged/telescopic builders - compile-time safety
@Value.Style(stagedBuilder = true)
@Value.Immutable
public interface Connection {
String host();
int port();
@Value.Default
default int timeout() { return 30; }
}
// Staged builder enforces mandatory attributes first
ImmutableConnection connection = ImmutableConnection.builder()
.host("localhost") // Must set host first
.port(8080) // Then port
.timeout(60) // Optional attributes can be set in any order
.build();
// Enhanced builder methods
@Value.Style(
canBuild = "isReady",
buildOrThrow = "buildOrThrow"
)
@Value.Immutable
public interface Request {
String method();
String url();
}
ImmutableRequest.Builder builder = ImmutableRequest.builder();
builder.method("GET");
if (builder.isReady()) { // Check if can build
Request req = builder.build();
} else {
Request req = builder.buildOrThrow(() ->
new IllegalStateException("Missing required fields"));
}Configure validation behavior for generated objects.
/**
* Validation method options for generated immutable objects
*/
enum ValidationMethod {
NONE, // No validation, allow nulls and missing required fields
MANDATORY_ONLY, // Check required fields provided (even if null)
SIMPLE, // Standard null-hostile validation (default)
VALIDATION_API // Use JSR 303 Bean Validation API
}Usage Examples:
// Disable validation completely
@Value.Style(validationMethod = ValidationMethod.NONE)
@Value.Immutable
public interface Lenient {
String name(); // Can be null
int count(); // Will be 0 if not provided
}
// Lenient validation allows nulls and missing fields
Lenient lenient = ImmutableLenient.builder().build(); // No errors
// Mandatory-only validation
@Value.Style(validationMethod = ValidationMethod.MANDATORY_ONLY)
@Value.Immutable
public interface MandatoryCheck {
String name();
@Value.Default
default int count() { return 0; }
}
// Must provide name (even if null), count is optional
MandatoryCheck obj1 = ImmutableMandatoryCheck.builder()
.name(null) // Allowed - null is provided
.build();
// MandatoryCheck obj2 = ImmutableMandatoryCheck.builder().build(); // Error - name not provided
// JSR 303 Bean Validation
@Value.Style(validationMethod = ValidationMethod.VALIDATION_API)
@Value.Immutable
public interface ValidatedUser {
@NotNull @Size(min = 2)
String name();
@NotNull @Email
String email();
@Min(0) @Max(120)
int age();
}
// Uses Bean Validation annotations for validation
ValidatedUser user = ImmutableValidatedUser.builder()
.name("John")
.email("john@example.com")
.age(25)
.build(); // Validates constraintsControl the visibility of generated classes and methods.
/**
* Implementation visibility options
*/
enum ImplementationVisibility {
PUBLIC, // Generated class forced to be public
SAME, // Same visibility as abstract type
SAME_NON_RETURNED, // Same visibility, abstract type returned from factories
PACKAGE, // Package visibility
PRIVATE // Private visibility (requires builder or enclosing)
}
enum BuilderVisibility {
PUBLIC, // Builder forced to be public
SAME, // Same visibility as abstract type
PACKAGE // Package visibility
}Usage Examples:
// Private implementation with public builder
@Value.Style(
visibility = ImplementationVisibility.PRIVATE,
builderVisibility = BuilderVisibility.PUBLIC
)
@Value.Immutable
public interface PrivateImpl {
String value();
}
// Implementation class is private, only accessible via builder
// PrivateImpl obj = ImmutablePrivateImpl.of("test"); // Not accessible
PrivateImpl obj = ImmutablePrivateImpl.builder().value("test").build(); // OK
// Package-private implementation
@Value.Style(visibility = ImplementationVisibility.PACKAGE)
@Value.Immutable
public interface PackagePrivate {
String data();
}
// Generated ImmutablePackagePrivate has package visibilitySpecialized configuration for complex scenarios and performance optimization.
/**
* Advanced configuration options
*/
boolean allParameters() default false; // All attributes become parameters
boolean deepImmutablesDetection() default false; // Enhanced type analysis
boolean jdkOnly() default false; // Use only JDK collections
boolean jdk9Collections() default false; // Use JDK 9+ immutable collections
boolean depluralize() default false; // Depluralize collection method names
boolean weakInterning() default false; // Use weak references for interning
boolean forceJacksonPropertyNames() default true; // Force Jackson property namesUsage Examples:
// All parameters style for tuple-like objects
@Value.Style(
allParameters = true,
typeImmutable = "*Tuple",
defaults = @Value.Immutable(builder = false)
)
@Value.Immutable
public interface Point {
double x();
double y();
@Value.Parameter(false) // Exclude from parameters
@Value.Derived
default double magnitude() {
return Math.sqrt(x() * x() + y() * y());
}
}
// Only constructor available: ImmutablePointTuple.of(x, y)
Point point = ImmutablePointTuple.of(3.0, 4.0);
// Deep immutables detection for enhanced builder methods
@Value.Style(deepImmutablesDetection = true)
@Value.Immutable
public interface Container {
Point location();
List<Point> waypoints();
}
// Enhanced builder with shortcut methods
Container container = ImmutableContainer.builder()
.location(3.0, 4.0) // Shortcut for ImmutablePoint.of(3.0, 4.0)
.addWaypoints(1.0, 1.0) // Shortcut for waypoints list
.addWaypoints(2.0, 2.0)
.build();
// Collection method depluralization
@Value.Style(
depluralize = true,
depluralizeDictionary = {"person:people", "child:children"}
)
@Value.Immutable
public interface Family {
List<String> people(); // addPerson(), addAllPeople()
List<String> children(); // addChild(), addAllChildren()
List<String> items(); // addItem(), addAllItems()
}
ImmutableFamily family = ImmutableFamily.builder()
.addPerson("Alice") // Singular form
.addChild("Bob") // Custom depluralization
.addItem("laptop") // Standard depluralization
.build();Install with Tessl CLI
npx tessl i tessl/maven-org-immutables--value