9

您将如何为具有接口成员变量的类编写复制构造函数?

例如:

public class House{

    // IAnimal is an interface
    IAnimal pet;

    public House(IAnimal pet){
        this.pet = pet;
    }

    // my (non-working) attempt at a copy constructor
    public House(House houseIn){
        // The following line doesn't work because IAnimal (an interface) doesn't 
        // have a copy constructor
        this.pet = new IAnimal(houseIn.pet);
    }
}

我是否被迫有一个混凝土Animal如果是这样,似乎将类重用于有狗的房子和有猫的房子会变得很复杂!

4

4 回答 4

7

您有以下三种选择之一:

  1. 有一个方法IAnimal来深度克隆对象(由诸如 DOM 接口之类的库使用Node.cloneNode(boolean)
  2. 在所有实现中创建一个复制构造函数,该构造函数IAnimal采用具体类型并在接口契约中提出要求,然后使用反射来访问它
  3. 创建一个复制工厂,手动复制每个实现
  4. 使用 3rd-party 库,它通过自己的合约为您实现深度克隆,例如无参数构造函数、非最终字段、Serializable类等,就像这里列出的一样

复制方法

对于#1,请执行以下操作:

public interface IAnimal {
    IAnimal cloneDeep();
}

在您的具体类型中实现它,然后调用该方法来复制它:

this.pet = pet.cloneDeep();

然后在界面中记录需求,大致如下:

此接口的实现必须返回一个不==属于此实例的对象,并且必须进行深度克隆,以便对该对象的操作不会导致对返回的对象的操作,反之亦然。

为了与接口兼容,实现必须遵循这个契约,但这不会在编译时强制执行。

复制构造函数

尝试反射性地访问复制构造函数,然后声明接口中的所有具体实现都需要复制构造函数,这成为接口契约的一部分。然后每个实现将如下所示:

public class Dog implements IAnimal {

    private String name;

    public Dog(Dog dog) {
        this.name = dog.name;
    }
}

然后你只需要一个方法来复制每个实现:

public static <A extends IAnimal> A copy(A animal) {
    Class<?> animalType = animal.getClass();
    // This next line throws a number of checked exceptions you need to catch
    return (A) animalType.getConstructor(animalType).newInstance(animal);
}

如果你有这个,在你的界面文档中添加一个声明来实现这个效果:

此接口的实现必须定义一个复制构造函数,该构造函数接受与其类相同类型或超类型的参数。此构造函数必须制作参数的深层副本,以便对该对象的操作不会导致对返回的对象的操作,反之亦然。

同样,这是运行时强制执行的。当构造函数不存在时,上面的copy方法会抛出错误。NoSuchMethodException

复制工厂

这需要IAnimal并使用instanceof来决定将其传递给哪个方法,例如:

public static IAnimal copyAnimal(IAnimal animal) {
    if (animal instanceof Dog)
        return copyDog((Dog) animal);
    if (animal instanceof Cat)
        return copyCat((Cat) animal);
    //...
    else
        throw new IllegalArgumentException("Could not copy animal of type: "
                + animal.getClass().getName());
}

copy然后手动对每种类型的方法进行深度复制。

于 2013-06-12T21:48:58.207 回答
2

如果我理解你的问题,由于你不能在接口中指定构造函数,你需要在你的接口中声明一个深拷贝方法并在你的类中实现它。您不能实例化接口。House根据您的需要,您可能还想深度复制任何内容。

public interface IAnimal {
    ...
    IAnimal deepCopy(); 
}


public House(House houseIn){
    this.pet = houseIn.pet.deepCopy();
}

当然,问题在于您是否可以避免做错事。这有点像你在这里并不真正想要一个接口,而是一个抽象类。

于 2013-06-12T21:39:11.713 回答
1

好的,让我解释一下这段代码中的一个问题。请注意,当您有一个接口时,您定义的是一种行为,而不是一个对象(或生物)的抽象或身份,例如动物。在这种情况下,您需要一个抽象类而不是接口。那开始说。您不能在接口上使用构造函数(请参阅为什么我们不允许在接口中指定构造函数?)因此以这种方式使用接口将失败。

所以在这种情况下,我建议改用抽象类:

public class House {

AbstractAnimal pet;

public House(AbstractAnimal pet) {
    this.pet = pet;
}

public House(House houseIn) {
    this.pet = new AbstractAnimal(houseIn.pet) {
        //implement the abstract methods that are required for anonymous class
    };
}

private abstract class AbstractAnimal {
    //Abstract class attributes 

    public AbstractAnimal(AbstractAnimal Parent) {
        //Constructor code, can also call abstract methods if required
    }
    //declare Abstract methods if required.
}
于 2013-06-12T21:45:51.820 回答
0

据我所知,Java 中没有直接的等价物。

“正确”的方法是让接口自己实现Cloanable

最简单的方法可能是使用反射。我知道有一个库可以处理任意对象的深层副本,但我现在想不起这个名字。

相关:Java:深度克隆/复制实例的推荐解决方案

于 2013-06-12T21:33:34.817 回答