Compile-time annotation processor for generating immutable value objects with builder patterns and compile-time validation.
—
Specialized features including modifiable companions, collection ordering, inclusion patterns, enclosing type organization, and extended generation capabilities for complex scenarios.
Generate mutable companion classes that can be converted to and from immutable instances.
/**
* Generate modifiable implementation of abstract value class.
* Provides over-flexible builder or partially built representation.
* Generated with "Modifiable" prefix by default.
*/
@interface Value.Modifiable { }Usage Examples:
@Value.Immutable
@Value.Modifiable
public interface Person {
String name();
int age();
List<String> hobbies();
Optional<String> email();
}
// Both immutable and modifiable implementations generated
// ImmutablePerson - immutable implementation
// ModifiablePerson - mutable implementation
// Create modifiable instance
ModifiablePerson modifiable = ModifiablePerson.create()
.setName("Alice")
.setAge(25)
.addHobbies("reading")
.setEmail("alice@example.com");
// Modify values
modifiable.setAge(26);
modifiable.addHobbies("swimming", "hiking");
modifiable.getHobbies().add("cooking"); // Direct collection access
// Convert to immutable
Person immutable = modifiable.toImmutable();
// Convert back to modifiable for further changes
ModifiablePerson anotherModifiable = ModifiablePerson.copyOf(immutable);
// Modifiable from builder pattern
ModifiablePerson fromBuilder = ModifiablePerson.create()
.from(immutable) // Initialize from existing instance
.setAge(27);Control the ordering behavior of generated collection implementations.
/**
* Specify natural ordering for implemented SortedSet, NavigableSet,
* SortedMap, or NavigableMap. Elements must implement Comparable.
*/
@interface Value.NaturalOrder { }
/**
* Specify reversed natural ordering for sorted collections.
* Elements must implement Comparable.
*/
@interface Value.ReverseOrder { }Usage Examples:
@Value.Immutable
public interface SortedData {
// Natural ordering - ascending
@Value.NaturalOrder
SortedSet<String> names();
// Reverse ordering - descending
@Value.ReverseOrder
SortedSet<Integer> scores();
// Natural ordering for map
@Value.NaturalOrder
SortedMap<String, Integer> rankings();
// Regular collections (no special ordering)
List<String> tags();
Set<String> categories();
}
// Usage with automatic ordering
SortedData data = ImmutableSortedData.builder()
.addNames("Charlie", "Alice", "Bob") // Stored as: Alice, Bob, Charlie
.addScores(85, 92, 78) // Stored as: 92, 85, 78 (descending)
.putRankings("Alice", 1)
.putRankings("Bob", 2) // Stored by key order: Alice, Bob
.addTags("urgent", "review", "feature") // Insertion order preserved
.build();
// Accessing ordered collections
SortedSet<String> orderedNames = data.names(); // [Alice, Bob, Charlie]
SortedSet<Integer> orderedScores = data.scores(); // [92, 85, 78]Include external types in generation without modifying their source code.
/**
* Includes specified abstract value types into generation processing.
* Used to generate immutable implementations of classes from different
* packages where source code cannot be changed.
*/
@interface Value.Include {
Class<?>[] value();
}Usage Examples:
// External interface that cannot be modified
package com.external.library;
public interface ExternalConfig {
String getHost();
int getPort();
boolean isSecure();
}
// Include external types for generation
package com.myapp.config;
@Value.Include({
com.external.library.ExternalConfig.class,
com.external.library.DatabaseSettings.class
})
@Value.Immutable
public interface MyConfig extends ExternalConfig {
@Override
default String getHost() { return "localhost"; }
@Override
default int getPort() { return 8080; }
@Override
default boolean isSecure() { return false; }
// Additional attributes specific to MyConfig
String applicationName();
List<String> allowedOrigins();
}
// Generated: ImmutableMyConfig implementing both interfaces
MyConfig config = ImmutableMyConfig.builder()
.host("production.example.com")
.port(443)
.secure(true)
.applicationName("MyApp")
.addAllowedOrigins("https://app.example.com", "https://admin.example.com")
.build();
// Works with external interface type
ExternalConfig external = config; // Upcasting works
String host = external.getHost();Organize generated classes under umbrella top-level classes for better namespace management.
/**
* Applied to top level class containing nested abstract value types
* to provide namespacing. Generated implementation classes will be
* nested under "Immutable" prefixed umbrella class.
*/
@interface Value.Enclosing { }Usage Examples:
@Value.Enclosing
public class GraphPrimitives {
@Value.Immutable
public interface Vertex {
String id();
double x();
double y();
@Value.Default
default String label() { return id(); }
}
@Value.Immutable
public interface Edge {
String id();
Vertex source();
Vertex target();
@Value.Default
default double weight() { return 1.0; }
}
@Value.Immutable
public interface Graph {
Set<Vertex> vertices();
Set<Edge> edges();
@Value.Check
protected void validateGraph() {
// Ensure all edge endpoints exist in vertex set
Set<String> vertexIds = vertices().stream()
.map(Vertex::id)
.collect(Collectors.toSet());
for (Edge edge : edges()) {
if (!vertexIds.contains(edge.source().id()) ||
!vertexIds.contains(edge.target().id())) {
throw new IllegalStateException(
"Edge " + edge.id() + " references non-existent vertex"
);
}
}
}
}
}
// Generated umbrella class: ImmutableGraphPrimitives
// Generated nested classes:
// - ImmutableGraphPrimitives.Vertex
// - ImmutableGraphPrimitives.Edge
// - ImmutableGraphPrimitives.Graph
// Usage with clean imports
import static com.example.ImmutableGraphPrimitives.*;
// Simple class names without "Immutable" prefix
Vertex v1 = Vertex.builder().id("v1").x(0).y(0).build();
Vertex v2 = Vertex.builder().id("v2").x(1).y(1).build();
Edge edge = Edge.builder()
.id("e1")
.source(v1)
.target(v2)
.weight(2.5)
.build();
Graph graph = Graph.builder()
.addVertices(v1, v2)
.addEdges(edge)
.build();Enhanced builder functionality for complex construction scenarios.
/**
* Style configuration for advanced builder features
*/
@interface Value.Style {
/**
* Generate attribute builder methods for discoverable nested builders.
* Enables fluent construction of nested immutable objects.
*/
boolean attributeBuilderDetection() default false;
/**
* Patterns for detecting builder methods on attribute types.
*/
String[] attributeBuilder() default {"Builder", "*Builder", "builder", "new"};
/**
* Generate toBuilder() method on immutable instances.
*/
String toBuilder() default "";
}Usage Examples:
// Nested builders with attribute builder detection
@Value.Style(
attributeBuilderDetection = true,
toBuilder = "toBuilder"
)
@Value.Immutable
public interface Order {
Customer customer();
List<OrderItem> items();
Address shippingAddress();
}
@Value.Immutable
public interface Customer {
String name();
String email();
static Builder builder() { return ImmutableCustomer.builder(); }
}
@Value.Immutable
public interface OrderItem {
String productId();
int quantity();
BigDecimal price();
static Builder builder() { return ImmutableOrderItem.builder(); }
}
// Enhanced builder with nested builder methods
Order order = ImmutableOrder.builder()
.customerBuilder() // Returns Customer.Builder
.name("John Doe")
.email("john@example.com")
.addItemsBuilder() // Returns OrderItem.Builder for collection
.productId("LAPTOP-001")
.quantity(1)
.price(new BigDecimal("999.99"))
.addItemsBuilder() // Another item builder
.productId("MOUSE-001")
.quantity(2)
.price(new BigDecimal("25.99"))
.shippingAddressBuilder() // Returns Address.Builder
.street("123 Main St")
.city("Springfield")
.zipCode("12345")
.build();
// toBuilder() for creating modified copies
Order.Builder orderBuilder = order.toBuilder();
Order updatedOrder = orderBuilder
.customerBuilder()
.email("john.doe@example.com") // Update customer email
.build();Built-in support for various serialization frameworks and custom serialization patterns.
Usage Examples:
// Jackson JSON serialization
@Value.Immutable
@JsonSerialize(as = ImmutableUser.class)
@JsonDeserialize(as = ImmutableUser.class)
public interface User {
@JsonProperty("user_name")
String name();
@JsonProperty("user_age")
int age();
@JsonIgnore
@Value.Auxiliary
long internalId();
}
// Java serialization
@Value.Immutable
public interface SerializableData implements Serializable {
String data();
int version();
@Value.Auxiliary // Excluded from serialization
transient long timestamp();
}
// Custom serialization with Style
@Value.Style(
forceJacksonPropertyNames = false, // Use custom naming strategy
forceJacksonIgnoreFields = true // Add @JsonIgnore to fields
)
@Value.Immutable
public interface CustomSerialization {
String publicData();
@Value.Redacted
String sensitiveData();
}Install with Tessl CLI
npx tessl i tessl/maven-org-immutables--value