Java中类似JavaScript Object.assign的非修改原对象替代方案
当然有啦!Java里要实现这种不可变对象的逐步属性填充,核心思路和JS里的Object.assign完全一致:每次更新属性都返回一个全新的对象,原对象保持纹丝不动。下面给你几种常用的靠谱方案:
Java中实现不可变对象属性更新的方法
1. 手动实现「拷贝+更新」方法(最基础可控)
自己给不可变类写返回新对象的属性更新方法,完全不依赖外部库,逻辑清晰可控。举个Person类的例子:
public final class Person { // 所有属性设为final,保证不可变 private final String name; private final Integer age; private final String email; // 全参构造器(用于创建新对象) public Person(String name, Integer age, String email) { this.name = name; this.age = age; this.email = email; } // 为每个属性提供更新方法,返回全新对象 public Person withName(String name) { return new Person(name, this.age, this.email); } public Person withAge(Integer age) { return new Person(this.name, age, this.email); } public Person withEmail(String email) { return new Person(this.name, this.age, email); } // 只提供getter,不写setter public String getName() { return name; } public Integer getAge() { return age; } public String getEmail() { return email; } }
使用起来和JS的Object.assign体验类似:
// 初始全null的对象 Person initialPerson = new Person(null, null, null); // 逐步填充,每次都生成新对象,原对象不受影响 Person personWithName = initialPerson.withName("Alice"); Person personWithNameAndAge = personWithName.withAge(30); Person finalPerson = personWithNameAndAge.withEmail("alice@example.com");
2. Builder模式(适合属性较多的场景)
如果你的Person类属性很多,手动写一堆withXxx方法会很繁琐,用Builder模式更优雅——可以基于现有对象生成Builder,修改属性后再构建新对象:
public final class Person { private final String name; private final Integer age; private final String email; // 私有构造器,只能通过Builder创建 private Person(Builder builder) { this.name = builder.name; this.age = builder.age; this.email = builder.email; } // 基于现有对象生成Builder的方法 public Builder toBuilder() { return new Builder() .name(this.name) .age(this.age) .email(this.email); } // getter方法 public String getName() { return name; } public Integer getAge() { return age; } public String getEmail() { return email; } // 内部Builder类 public static class Builder { private String name; private Integer age; private String email; public Builder name(String name) { this.name = name; return this; } public Builder age(Integer age) { this.age = age; return this; } public Builder email(String email) { this.email = email; return this; } public Person build() { return new Person(this); } } }
使用示例:
// 初始全null对象 Person initialPerson = new Person.Builder().build(); // 基于原对象修改属性,生成新对象 Person personWithName = initialPerson.toBuilder().name("Bob").build(); Person finalPerson = personWithName.toBuilder().age(25).email("bob@example.com").build();
3. 用Lombok简化样板代码(高效省心)
如果项目里已经在用Lombok,那直接用@Value(生成不可变类)+@With(自动生成withXxx方法),几行代码搞定:
import lombok.Value; import lombok.With; @Value // 自动生成final类、final属性、全参构造器、getter等 @With // 自动为所有属性生成返回新对象的withXxx方法 public class Person { String name; Integer age; String email; }
使用方式和第一种方案完全一样,但代码量直接砍到最少,非常省心。
4. Java 16+ Record类(官方原生不可变方案)
Java 16引入的Record是官方专门为不可变数据类设计的,你可以手动为它添加withXxx方法,或者配合Lombok的@With进一步简化:
// 手动添加with方法的Record public record Person(String name, Integer age, String email) { public Person withName(String name) { return new Person(name, this.age, this.email); } public Person withAge(Integer age) { return new Person(this.name, age, this.email); } public Person withEmail(String email) { return new Person(this.name, this.age, email); } }
如果用Lombok的@With,代码会更简洁:
import lombok.With; @With public record Person(String name, Integer age, String email) {}
Record本身天生不可变,完美契合你的核心需求。
总结
这些方案的核心都是绝不修改原对象,每次属性更新都返回全新的不可变对象,和JavaScript里Object.assign的设计思路完全匹配。如果不想依赖第三方库,手动写withXxx方法或Builder模式就足够;如果想减少样板代码,Lombok是绝佳选择;Java 16+的Record则是官方推荐的原生方案。
内容的提问来源于stack exchange,提问作者Prashanth
相关产品推荐
相关产品推荐

