Java 中的拷贝构造方法

译者注:https://www.baeldung.com/ 是一个文章质量非常高的英文技术网站,感兴趣的同学可以去访问学习。

1.介绍

Java 中的拷贝构造方法是一种使用该类的一个对象构造另外一个对象的构造方法。

当需要拷贝一个带有多个成员变量的复杂对象或者想构造已存在对象的深拷贝对象时非常有用。

译者注:本文内容很简单,但是很实用。拷贝构造方法实战中用虽然用的不多,但是是一个非常不错的技巧。

2.如何创造拷贝构造方法

要创建拷贝构造方法,首先需要声明带有和本类相同类型的参数构造函数:

public class Employee {
    private int id;
    private String name;
   
    public Employee(Employee employee) {
    }
}

然后,将参数对象的每个属性都复制给新的实例。

public class Employee {
    private int id;
    private String name;
     
    public Employee(Employee employee) {
        this.id = employee.id;
        this.name = employee.name;
    }
}

上面的做法属于浅拷贝。

上面定义的属性不是int 就是 String, 只包含基本类型和不可变类型,因此使用前拷贝就没问题。

但是如果类中包含可变类型就要通过该构造函数实现深拷贝。

为了实现深拷贝,我们需要根据原始可变对象类型构造新的实例。

public class Employee {
    private int id;
    private String name;
    private Date startDate;
 
    public Employee(Employee employee) {
        this.id = employee.id;
        this.name = employee.name;
        this.startDate = new Date(employee.startDate.getTime());
    }
}

3.拷贝构造方法 VS Clone

在 Java 中,我们还可以使用 clone 方法实现根据已有对象创建新对象。

但是拷贝构造方法更有优势:

  • 拷贝构造方法实现更简单。不需要实现 Cloneable 接口,也不需要处理 CloneNotSupportedException

  • clone 函数返回一个普通的 Object 类型的引用。还需要转成特定的类型。

  • 在 clone 方法中不能为 final 属性赋值,但是在拷贝构造方法中就可以。

4.继承问题

Java 中的拷贝构造方法不会被子类继承。

因此,如果我们尝试初始化一个带有父类引用的子类对象,就会面临着类型转换问题。

为了更好地说明这个问题,我们首先创建 Employee的子类型和拷贝构造方法。

public class Manager extends Employee {
    private List<Employee> directReports;
    // ... 其他构造方法
 
    public Manager(Manager manager) {
        super(manager.id, manager.name, manager.startDate);
        this.directReports = directReports.stream()
          .collect(Collectors.toList());
    }
}

然后,我们声明一个 Employee 类型的引用指向通过 Manager 构造方法构造的 Manager 实例。

Employee source = new Manager(1, "Baeldung Manager", startDate, directReports);

由于引用类型为 Employee, 如果我们想使用 Manager 的拷贝构造函数就必须将 source 强转为 Manager 类型。

Employee clone = new Manager((Manager) source);

如果参数不是 Manager 类型,运行时会抛出 ClassCastException。

其中一种避免使用拷贝构造方法时类型转换的方法是创建一个继承的拷贝函数:

public class Employee {
   public Employee copy() {
        return new Employee(this);
    }
}
 
public class Manager extends Employee {
    @Override
    public Employee copy() {
        return new Manager(this);
    }
}

在每个类的拷贝函数中调用自己类型的拷贝构造函数即可。

这样就保证了生成的对象和调用的对象类型相同。

Employee clone = source.copy();

5.结论

本文介绍了拷贝构造方法,给出了避免使用 clone 函数的原因。

如果引用类型为父类型而实际对象类型为子类型时,使用子类型的拷贝构造函数需要将父类型强制类型转换为子类型,容易出现转换问题。本文也针对这个问题提供了解决方案。

文章配套源码:github tutorials


如果你觉得本文对你有帮助,欢迎点赞、转发、评论,你的支持是我创作的最大动力。

发布了379 篇原创文章 · 获赞 862 · 访问量 132万+

猜你喜欢

转载自blog.csdn.net/w605283073/article/details/103639889