Можно ли написать конструкторы копирования для классов с переменными-членами интерфейса в Java?
Как бы вы написали конструктор копирования для класса с переменными-членами интерфейса?
Например:
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 ответа
У вас есть один из трех вариантов:
- Есть метод на
IAnimal
для глубокого клонирования объекта (используется библиотеками, такими как интерфейсы DOM, такие какNode.cloneNode(boolean)
) - Создайте конструктор копирования во всех реализациях
IAnimal
это берет конкретный тип и делает это требованием в контракте интерфейса, затем использует отражение для доступа к нему - Создайте фабрику копирования, которая будет копировать каждую реализацию вручную
- Используйте стороннюю библиотеку, которая реализует глубокое клонирование для вас со своими собственными контрактами, такими как конструкторы без аргументов, неконечные поля,
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
методы для каждого типа вручную.
Если я понимаю вашу проблему, поскольку вы не можете указывать конструкторы в интерфейсе, вам нужно объявить метод глубокого копирования в вашем интерфейсе и реализовать его в ваших классах. Вы не можете создать экземпляр интерфейса. Возможно, вы также захотите глубоко скопировать что-нибудь в House
а также, в зависимости от ваших потребностей.
public interface IAnimal {
...
IAnimal deepCopy();
}
public House(House houseIn){
this.pet = houseIn.pet.deepCopy();
}
Проблема, конечно, в том, что вы должны сделать так, чтобы не сделать что-то не так. Это пахнет так, будто вам не нужен интерфейс, а скорее абстрактный класс.
Хорошо, позвольте мне объяснить проблему в этом коде. обратите внимание, что когда у вас есть интерфейс, вы определяете поведение, а не абстракцию или идентичность объекта (или существа), такого как животное. в этом случае вам нужен абстрактный класс вместо интерфейса. что началось сказано. у вас не может быть конструктора в интерфейсе (см. Почему нам не разрешено указывать конструктор в интерфейсе?), поэтому использование интерфейса таким способом завершится неудачей.
В этом случае я бы предложил использовать абстрактный класс:
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.
}
Насколько мне известно, в Java нет прямого эквивалента этому.
"Правильный" способ - сделать интерфейс самостоятельно реализующим Cloanable.
И самый простой способ - использовать отражение. Я знаю, что есть библиотека, которая обрабатывает глубокие копии произвольных объектов, но я не могу вспомнить имя в данный момент.
Связанный: Java: рекомендуемое решение для глубокого клонирования / копирования экземпляра