求Java多成员变量类的简洁拷贝构造函数实现方案
你提到的场景非常典型:当类只有少量成员变量时,拷贝构造函数里逐行赋值完全可行,但成员变量一多,这种写法就变得冗余且容易出错。而且你尝试直接给this赋值失败,这是因为**this在构造函数中是指向当前正在初始化的对象的final引用,无法重新指向另一个已存在的对象实例**,所以只能通过给成员变量逐个赋值的方式完成拷贝。
下面是几种能帮你实现简洁拷贝的方案:
1. 利用Java反射机制批量赋值
反射可以让我们动态遍历类的成员变量,自动完成赋值,不用手动写每一行代码。这种方式不依赖第三方库,适合自定义需求:
import java.lang.reflect.Field; class Obj { int a, b; // 普通构造函数 Obj(int a, int b) { this.a = a; this.b = b; } // 基于反射的拷贝构造函数 Obj(Obj obj1) { try { // 获取当前类的所有成员变量(包括私有变量) Field[] fields = this.getClass().getDeclaredFields(); for (Field field : fields) { // 设置访问权限,允许修改私有成员 field.setAccessible(true); // 从源对象取值,赋值给当前对象的对应成员 field.set(this, field.get(obj1)); } } catch (IllegalAccessException e) { e.printStackTrace(); } } } class CopyObj2 { public static void main(String[] args) { Obj obj1 = new Obj(5, 10); System.out.println("Obj1\t"+obj1.a+", "+obj1.b); Obj obj2 = new Obj(obj1); System.out.println("Obj2\t"+obj2.a+", "+obj2.b); } }
注意:如果类里有继承来的成员变量,需要用getFields()(只能获取public的继承成员)或者递归遍历父类的字段来处理。
2. 使用第三方Bean工具类(一行代码搞定)
很多开源库都封装了对象拷贝的逻辑,比如Apache Commons BeanUtils或者Spring BeanUtils,它们底层也是用反射,但封装得更易用:
以Apache Commons BeanUtils为例,先确保引入依赖,然后拷贝构造函数可以写成:
import org.apache.commons.beanutils.BeanUtils; class Obj { int a, b; Obj(int a, int b) { this.a = a; this.b = b; } Obj(Obj obj1) { try { // 一行代码完成所有成员变量的拷贝 BeanUtils.copyProperties(this, obj1); } catch (Exception e) { e.printStackTrace(); } } }
这种方式最简洁,但需要引入第三方依赖,适合已经使用这些库的项目。
3. Java 16+:使用记录类(Record)
如果你的项目使用Java 16或更高版本,可以把类定义为记录类,记录类会自动生成包含所有成员的拷贝方法(copy(),Java 17+支持),或者直接通过解构来快速拷贝:
// 定义记录类,自动生成构造函数、equals、hashCode、toString,以及拷贝相关方法 record Obj(int a, int b) { // 拷贝构造函数可以简化为: Obj(Obj obj1) { this(obj1.a, obj1.b); } // 或者Java 17+可以直接用copy方法: // Obj copy() { // return new Obj(this.a, this.b); // } } class CopyObj2 { public static void main(String[] args) { Obj obj1 = new Obj(5, 10); System.out.println("Obj1\t"+obj1.a()+", "+obj1.b()); Obj obj2 = new Obj(obj1); System.out.println("Obj2\t"+obj2.a()+", "+obj2.b()); } }
记录类本身就是为不可变数据载体设计的,拷贝逻辑会更简洁安全。
补充:为什么不能直接给this赋值?
在Java中,this是一个隐含的、指向当前对象的final引用,它的作用是让你访问当前对象的成员,而不是用来重新赋值的。在构造函数中,this指向的是正在被创建的新对象,你只能修改这个对象的成员变量,不能把它改成指向另一个已存在的对象(比如obj1),这就是你尝试给this赋值失败的原因。
内容的提问来源于stack exchange,提问作者Sai Kiran

