0
# Immutable Patterns
1
2
Generate immutable "wither" methods that create copies of objects with modified field values. The `@With` annotation provides functional-style object copying while maintaining immutability.
3
4
## Capabilities
5
6
### @With Annotation
7
8
Generates wither methods that create a copy of the object with one field changed, enabling immutable object updates.
9
10
```java { .api }
11
/**
12
* Put on any field to make lombok build a 'with' - a withX method which produces a clone of this object
13
* (except for 1 field which gets a new value).
14
*
15
* This annotation can also be applied to a class, in which case it'll be as if all non-static fields
16
* that don't already have a @With annotation have the annotation.
17
*/
18
@Target({ElementType.FIELD, ElementType.TYPE})
19
@Retention(RetentionPolicy.SOURCE)
20
public @interface With {
21
/**
22
* If you want your with method to be non-public, you can specify an alternate access level here.
23
*
24
* @return The method will be generated with this access modifier.
25
*/
26
AccessLevel value() default AccessLevel.PUBLIC;
27
28
/**
29
* Any annotations listed here are put on the generated method.
30
* The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).
31
* up to JDK7:
32
* @With(onMethod=@__({@AnnotationsGoHere}))
33
* from JDK8:
34
* @With(onMethod_={@AnnotationsGohere}) // note the underscore after onMethod.
35
*
36
* @return List of annotations to apply to the generated method.
37
*/
38
AnyAnnotation[] onMethod() default {};
39
40
/**
41
* Any annotations listed here are put on the generated method's parameter.
42
* The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).
43
* up to JDK7:
44
* @With(onParam=@__({@AnnotationsGoHere}))
45
* from JDK8:
46
* @With(onParam_={@AnnotationsGohere}) // note the underscore after onParam.
47
*
48
* @return List of annotations to apply to the generated parameter in the method.
49
*/
50
AnyAnnotation[] onParam() default {};
51
}
52
```
53
54
**Usage Examples:**
55
56
```java
57
import lombok.With;
58
import lombok.AllArgsConstructor;
59
60
@AllArgsConstructor
61
public class Person {
62
@With private final String name;
63
@With private final int age;
64
private final String email;
65
}
66
67
// Generated methods available:
68
// - Person withName(String name)
69
// - Person withAge(int age)
70
71
// Usage
72
Person original = new Person("John Doe", 30, "john@example.com");
73
Person updated = original.withAge(31);
74
Person renamed = original.withName("Jane Doe");
75
76
System.out.println(original.getAge()); // 30
77
System.out.println(updated.getAge()); // 31
78
System.out.println(renamed.getName()); // Jane Doe
79
```
80
81
**Class-Level Application:**
82
83
```java
84
import lombok.With;
85
import lombok.Value;
86
87
@Value
88
@With
89
public class ImmutablePoint {
90
double x;
91
double y;
92
String label;
93
}
94
95
// Generated methods available:
96
// - ImmutablePoint withX(double x)
97
// - ImmutablePoint withY(double y)
98
// - ImmutablePoint withLabel(String label)
99
100
// Usage
101
ImmutablePoint origin = new ImmutablePoint(0.0, 0.0, "origin");
102
ImmutablePoint moved = origin.withX(10.0).withY(5.0);
103
ImmutablePoint relabeled = moved.withLabel("moved point");
104
```
105
106
## Advanced Usage
107
108
### Combining with @Value
109
110
```java
111
import lombok.Value;
112
import lombok.With;
113
114
@Value
115
@With
116
public class Configuration {
117
String host;
118
int port;
119
boolean enableSsl;
120
String username;
121
122
// All fields get wither methods
123
}
124
125
// Usage
126
Configuration config = new Configuration("localhost", 8080, false, "admin");
127
Configuration sslConfig = config.withEnableSsl(true).withPort(8443);
128
Configuration prodConfig = sslConfig.withHost("prod.server.com");
129
```
130
131
### Access Level Control
132
133
```java
134
import lombok.With;
135
import lombok.AccessLevel;
136
import lombok.AllArgsConstructor;
137
138
@AllArgsConstructor
139
public class Account {
140
@With(AccessLevel.PACKAGE) private final String accountId;
141
@With private final String name;
142
@With(AccessLevel.PRIVATE) private final double balance;
143
144
// Package-private: Account withAccountId(String accountId)
145
// Public: Account withName(String name)
146
// Private: Account withBalance(double balance)
147
148
public Account deposit(double amount) {
149
return withBalance(balance + amount);
150
}
151
152
public Account withdraw(double amount) {
153
return withBalance(balance - amount);
154
}
155
}
156
```
157
158
### Null Safety Integration
159
160
```java
161
import lombok.With;
162
import lombok.NonNull;
163
import lombok.AllArgsConstructor;
164
165
@AllArgsConstructor
166
public class User {
167
@With @NonNull private final String username;
168
@With private final String email;
169
@With private final Integer age;
170
}
171
172
// Generated method includes null check:
173
// public User withUsername(@NonNull String username) {
174
// if (username == null) throw new NullPointerException("username is marked @NonNull but is null");
175
// return this.username == username ? this : new User(username, this.email, this.age);
176
// }
177
```
178
179
## Generated Code Behavior
180
181
### Method Generation Pattern
182
183
For each `@With` annotated field, lombok generates a method with the pattern:
184
- **Method name**: `with` + capitalized field name
185
- **Parameter**: Same type as the field
186
- **Return type**: Same type as the containing class
187
- **Behavior**: Returns `this` if value equals current field value, otherwise creates new instance
188
189
### Performance Optimization
190
191
The generated wither methods include an optimization that returns the same instance if the new value equals the current value:
192
193
```java
194
public Person withAge(int age) {
195
return this.age == age ? this : new Person(this.name, age, this.email);
196
}
197
```
198
199
### Constructor Requirements
200
201
The `@With` annotation requires that a constructor exists that takes all fields as parameters in the same order they're declared in the class. This is automatically satisfied when using:
202
- `@AllArgsConstructor`
203
- `@Value`
204
- `@Data` (if no explicit constructors exist)
205
206
## Configuration
207
208
The `@With` annotation respects lombok configuration settings:
209
210
- `lombok.with.flagUsage`: Control usage warnings
211
- `lombok.accessors.fluent`: Affects generated method names when fluent accessors are enabled
212
- `lombok.accessors.chain`: Affects return type when chaining is enabled