Question

How would you write a copy constructor for a class with interface member variables?

For instance:

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);
    }
}

Am I forced to have a concrete Animal? If so, it seems reusing the class for houses with dogs vs. houses with cats becomes convoluted!

Was it helpful?

Solution

You have one of three choices:

  1. Have a method on IAnimal to deeply clone the object (used by libraries such as the DOM interfaces like Node.cloneNode(boolean))
  2. Create a copy constructor in all implementations of IAnimal that takes the concrete type and make that a requirement in the interface contract, then use reflection to access it
  3. Create a copy factory that will copy each implementation manually
  4. Use a 3rd-party library that implements deep cloning for you with its own contracts, such as no-args constructors, non-final fields, Serializable classes, etc., like the ones listed here

Copy Method

For #1, do something like:

public interface IAnimal {
    IAnimal cloneDeep();
}

Implement that in your concrete types, then invoke that method to copy it:

this.pet = pet.cloneDeep();

Then document the requirement in the interface, saying something along the lines of:

Implementations of this interface must return an object that is not == to this instance, and must be deeply cloned so that manipulation of this object does not lead to manipulation of the returned one and vice versa.

Implementations will have to follow this contract in order to be compliant with the interface, but this won't be enforced at compile time.

Copy Constructor

Try to access a copy constructor reflectively, then state that a copy constructor is required in all concrete implementations in the interface, which becomes part of the interface contract. Each implementation would then look like this:

public class Dog implements IAnimal {

    private String name;

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

And then all you need is a single method to copy every implementation:

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);
}

One you have this, add a statement to this effect in your interface's documentation:

Implementations of this interface must define a copy constructor that takes an argument of the same type or supertype of their class. This constructor must make a deep copy of the argument so that manipulation of this object does not lead to manipulation of the returned one and vice versa.

Again, this is runtime enforced. The copy method above throws NoSuchMethodException errors when the constructor doesn't exist.

Copy Factory

This takes the IAnimal and uses instanceof to decide which method to pass it to, like:

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());
}

Then do the deep copying in the copy methods for each type manually.

OTHER TIPS

If I understand your problem, Since you can't specify constructors in an interface you would need to declare a deep-copy method in your interface and implement it in your classes. You can't instantiate an interface. You possibly would also want to deep-copy anything in House as well, depending on your needs.

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


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

The problem of course, is that it's up to you to make that not do something wrong. It kinda smells like you don't really want an interface here, but rather an abstract class.

ok, let me explain a issue in this code. note that when you have a interface you are defining a behavior not an abstraction or identity of a object(or creature) such as an Animal. on this case you need an abstract class instead of a interface. that begin said. you cannot have a constructor on an interface( see Why are we not allowed to specify a constructor in an interface?) therefore using a interface that way will fail.

so on this case i would suggest using a abstract class instead:

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.
}

To my knowledge there is no direct equivalent to this in Java.

The "proper" way is to make the Interface it self implement Cloanable.

And the easiest way might be to use reflection. I know there is a library which handles deep copies of arbitrary objects, but I cannot recall the name at the moment.

Related: Java: recommended solution for deep cloning/copying an instance

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top