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!
You have one of three choices:
IAnimal
to deeply clone the object (used by libraries such as the DOM interfaces like Node.cloneNode(boolean)
)IAnimal
that takes the concrete type and make that a requirement in the interface contract, then use reflection to access itSerializable
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With