Generate immutable "wither" methods that create copies of objects with modified field values. The @With annotation provides functional-style object copying while maintaining immutability.
Generates wither methods that create a copy of the object with one field changed, enabling immutable object updates.
/**
* Put on any field to make lombok build a 'with' - a withX method which produces a clone of this object
* (except for 1 field which gets a new value).
*
* This annotation can also be applied to a class, in which case it'll be as if all non-static fields
* that don't already have a @With annotation have the annotation.
*/
@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface With {
/**
* If you want your with method to be non-public, you can specify an alternate access level here.
*
* @return The method will be generated with this access modifier.
*/
AccessLevel value() default AccessLevel.PUBLIC;
/**
* Any annotations listed here are put on the generated method.
* The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).
* up to JDK7:
* @With(onMethod=@__({@AnnotationsGoHere}))
* from JDK8:
* @With(onMethod_={@AnnotationsGohere}) // note the underscore after onMethod.
*
* @return List of annotations to apply to the generated method.
*/
AnyAnnotation[] onMethod() default {};
/**
* Any annotations listed here are put on the generated method's parameter.
* The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).
* up to JDK7:
* @With(onParam=@__({@AnnotationsGoHere}))
* from JDK8:
* @With(onParam_={@AnnotationsGohere}) // note the underscore after onParam.
*
* @return List of annotations to apply to the generated parameter in the method.
*/
AnyAnnotation[] onParam() default {};
}Usage Examples:
import lombok.With;
import lombok.AllArgsConstructor;
@AllArgsConstructor
public class Person {
@With private final String name;
@With private final int age;
private final String email;
}
// Generated methods available:
// - Person withName(String name)
// - Person withAge(int age)
// Usage
Person original = new Person("John Doe", 30, "john@example.com");
Person updated = original.withAge(31);
Person renamed = original.withName("Jane Doe");
System.out.println(original.getAge()); // 30
System.out.println(updated.getAge()); // 31
System.out.println(renamed.getName()); // Jane DoeClass-Level Application:
import lombok.With;
import lombok.Value;
@Value
@With
public class ImmutablePoint {
double x;
double y;
String label;
}
// Generated methods available:
// - ImmutablePoint withX(double x)
// - ImmutablePoint withY(double y)
// - ImmutablePoint withLabel(String label)
// Usage
ImmutablePoint origin = new ImmutablePoint(0.0, 0.0, "origin");
ImmutablePoint moved = origin.withX(10.0).withY(5.0);
ImmutablePoint relabeled = moved.withLabel("moved point");import lombok.Value;
import lombok.With;
@Value
@With
public class Configuration {
String host;
int port;
boolean enableSsl;
String username;
// All fields get wither methods
}
// Usage
Configuration config = new Configuration("localhost", 8080, false, "admin");
Configuration sslConfig = config.withEnableSsl(true).withPort(8443);
Configuration prodConfig = sslConfig.withHost("prod.server.com");import lombok.With;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
@AllArgsConstructor
public class Account {
@With(AccessLevel.PACKAGE) private final String accountId;
@With private final String name;
@With(AccessLevel.PRIVATE) private final double balance;
// Package-private: Account withAccountId(String accountId)
// Public: Account withName(String name)
// Private: Account withBalance(double balance)
public Account deposit(double amount) {
return withBalance(balance + amount);
}
public Account withdraw(double amount) {
return withBalance(balance - amount);
}
}import lombok.With;
import lombok.NonNull;
import lombok.AllArgsConstructor;
@AllArgsConstructor
public class User {
@With @NonNull private final String username;
@With private final String email;
@With private final Integer age;
}
// Generated method includes null check:
// public User withUsername(@NonNull String username) {
// if (username == null) throw new NullPointerException("username is marked @NonNull but is null");
// return this.username == username ? this : new User(username, this.email, this.age);
// }For each @With annotated field, lombok generates a method with the pattern:
with + capitalized field namethis if value equals current field value, otherwise creates new instanceThe generated wither methods include an optimization that returns the same instance if the new value equals the current value:
public Person withAge(int age) {
return this.age == age ? this : new Person(this.name, age, this.email);
}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:
@AllArgsConstructor@Value@Data (if no explicit constructors exist)The @With annotation respects lombok configuration settings:
lombok.with.flagUsage: Control usage warningslombok.accessors.fluent: Affects generated method names when fluent accessors are enabledlombok.accessors.chain: Affects return type when chaining is enabled