Java克隆-为什么连拷贝构造函数也不够用

This is third article in Java Cloning series, In previous two articles Java Cloning and Ťypes of Cloning (Shallow and Deep) in Details and Java Cloning - Copy Constructor versus Cloning, I had discussed Java cloning in detail and explained every concept like what is cloning, how does it work, what are the necessary steps we need to follow to implement cloning, how to use Object.clone(), what is Shallow and Deep cloning, how we can achieve cloning using Serialization and Copy constructors and advantages copy of copy constructors over Java cloning.

如果您已阅读这些文章,则可以轻松理解为什么在克隆或Object.clone()上使用Copy构造函数的好处。

在本文中,我将讨论为什么连拷贝构造函数也不够用?

Why-Copy-Constructors-Are-Not-Sufficient

是的,您没看错,复制构造函数本身并不足够,复制构造函数也不是多态的,因为构造函数不会从父类继承到子类。 如果尝试从父类引用中引用子对象,则在使用复制构造函数克隆它时会遇到问题。 为了理解它,让我们举两个人类在Mammal和Mammal上扩展的类的示例,Mammal类具有一个字段类型和两个构造函数,一个用于创建对象,一个副本构造器用于创建对象的副本。

class Mammal {

    protected String type;

    public Mammal(String type) { this.type = type; }

    public Mammal(Mammal original) { this.type = original.type; }

    public String getType() { return type; }

    public void setType(String type) { this.type = type; }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Mammal mammal = (Mammal) o;

        if (!type.equals(mammal.type)) return false;

        return true;
    }

    @Override
    public int hashCode() { return type.hashCode(); }

    @Override
    public String toString() {
        return "Mammal{" + "type='" + type + "'}";
    }
}

Human类扩展了哺乳动物类,具有一个名称字段,一个普通构造函数和一个副本构造函数来创建副本

class Human extends Mammal {

    protected String name;

    public Human(String type, String name) {
        super(type);
        this.name = name;
    }

    public Human(Human original) {
        super(original.type);
        this.name = original.name;
    }

    public String getName() { return name; }

    public void setName(String name) { this.name = name; }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        if (!super.equals(o)) return false;

        Human human = (Human) o;

        if (!type.equals(human.type)) return false;
        if (!name.equals(human.name)) return false;

        return true;
    }

    @Override
    public int hashCode() {
        int result = super.hashCode();
        result = 31 * result + name.hashCode();
        return result;
    }

    @Override
    public String toString() {
        return "Human{" + "type='" + type + "', name='" + name + "'}";
    }
}
在这两个复制构造函数中,我们都在进行深度克隆。

现在让我们为两个类创建对象

Mammal mammal = new Mammal("Human");
Human human = new Human("Human", "Naresh");

现在,如果要为哺乳动物或人类创建克隆,只需调用它们各自的拷贝构造函数即可

Mammal clonedMammal = new Mammal(mammal);
Human clonedHuman = new Human(human);

我们这样做不会出错,并且两个对象都将成功克隆,如下面的测试所示

System.out.println(mammal == clonedMammal); // false
System.out.println(mammal.equals(clonedMammal)); // true

System.out.println(human == clonedHuman); // false
System.out.println(human.equals(clonedHuman)); // true

但是,如果我们尝试从哺乳动物的参考中参考人类的对象,该怎么办?

Mammal mammalHuman = new Human("Human", "Mahesh");

为了克隆哺乳动物人类,我们不能使用构造函数人类,这会给我们带来编译错误,因为类型的哺乳动物人类是哺乳动物,人类类的构造函数接受人类。

Mammal clonedMammalHuman = new Human(mammalHuman); // compilation error

如果我们尝试使用Mammal的副本构造函数克隆哺乳动物人类,那么我们将获得哺乳动物的对象而不是人类,但是哺乳动物人类持有人类的对象

Mammal clonedMammalHuman = new Mammal(mammalHuman);

因此,哺乳动物人类和克隆哺乳动物人类都不是您在以下代码输出中看到的相同对象

System.out.println("Object " + mammalHuman + " and copied object " + clonedMammalHuman + " are == : " + (mammalHuman == clonedMammalHuman));
System.out.println("Object " + mammalHuman + " and copied object " + clonedMammalHuman + " are equal : " + (mammalHuman.equals(clonedMammalHuman)) + "\n");

输出:

Object Human{type='Human', name='Mahesh'} and copied object Mammal{type='Human'} are == : false
Object Human{type='Human', name='Mahesh'} and copied object Mammal{type='Human'} are equal : false

如我们所见,复制构造函数会遭受继承问题,并且它们也不是多态的。 那么,我们如何解决这个问题呢?有很多解决方案,例如创建静态Factory方法或创建一些通用类,这些都会为我们做到这一点,并且清单将继续存在?

但是有一个非常简单的解决方案,它将需要复制构造函数,并且也是多态的。 我们可以使用防御性复制方法来解决此问题,该方法将包含在我们的类中并从中调用复制构造函数,然后再次覆盖它的子类并从中调用其复制构造函数。

防御性复制方法还将为我们提供依赖注入的优势,我们可以注入依赖性,而不必使代码紧密耦合,而可以使其松散耦合,甚至可以创建一个接口来定义防御性复制方法,然后在我们的代码中实现 类并覆盖该方法。

因此,在哺乳动物类中,我们将创建一个无参数的方法cloneObject,但是我们可以随意命名此方法,例如clone或copy或copyInstance

public Mammal cloneObject() {
    return new Mammal(this);
}

我们可以在“人类”课程中覆盖相同的内容

@Override
public Human cloneObject() {
    return new Human(this);
}

如何克隆哺乳动物人类我们可以简单地说

Mammal clonedMammalHuman = mammalHuman.clone();

对于最后两个系统,我们将获得低于预期结果的输出。

Object Human{type='Human', name='Mahesh'} and copied object Human{type='Human', name='Mahesh'} are == : false
Object Human{type='Human', name='Mahesh'} and copied object Human{type='Human', name='Mahesh'} are equal : true

如我们所见,除了获得多态性的优势外,该选项还使我们可以自由传递任何参数。

You can find the complete source code for this article on this Github Repository and please feel free to provide your valuable feedback.

from: https://dev.to//njnareshjoshi/java-cloning-why-even-copy-constructors-are-not-sufficient-mlo

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值