Есть ли альтернативы реализации Clone в Java? - PullRequest
6 голосов
/ 27 апреля 2009

В моем Java-проекте у меня есть вектор различных типов трейдеров. Эти различные типы трейдеров являются подклассами класса Trader. Прямо сейчас у меня есть метод, который принимает трейдера в качестве аргумента и сохраняет его около 50 раз в векторе. У меня проблемы, потому что 50 раз хранить один и тот же объект - это просто хранить 50 ссылок одного и того же объекта. Мне нужно хранить 50 копий объекта. Я исследовал вопрос о реализации Clone , но я не хочу, чтобы программисты, определяющие тип Trader, беспокоились о том, чтобы сделать свой класс клонируемым. Кроме того, как указывает эта страница , реализация клона создает всевозможные проблемы. Я не думаю, что конструктор копирования тоже сработает, потому что, если я определю его в классе Trader, он не будет знать тип копируемого им трейдера и просто создаст общий трейдер. Что я могу сделать?

Редактировать: На самом деле я не хочу делать точные копии определенного объекта. Что я действительно пытаюсь сделать, так это добавить определенное количество трейдеров к вектору. Проблема заключается в том, что пользователю необходимо указать в аргументе, какой тип трейдера он хочет добавить. Вот пример того, что я пытаюсь сделать: (хотя мой синтаксис полностью мнимый)

public void addTraders(*traderType*)
{
    tradervect.add(new *traderType*())
}

Как мне добиться чего-то подобного в Java?

Ответы [ 5 ]

2 голосов
/ 27 апреля 2009

Мне немного неясно, почему вы захотите хранить около 50 клонов одного и того же объекта, если только они не являются первоначальным трейдером в качестве прототипа (см. Схему) для последующих трейдеров.

Если вы хотите сделать точную копию объекта, вы должны принять во внимание проблему полиморфизма. Если людям, относящимся к подклассам вашего заданного класса, разрешено добавлять члены состояния, то у вас уже достаточно головной боли с такими функциями, как equals и compareTo, что клон является еще одним случаем, когда вам требуется специальная обработка.

Я не согласен с тем, что клон - это всегда зло, иногда это необходимо. Однако в ситуациях с подклассами многие вещи становятся сложными.

Я бы порекомендовал вам прочитать (когда у вас есть возможность) «Эффективную Java» Блоха, которая охватывает многие его темы. Браха считает, что не стоит позволять другим продлевать ваши уроки, и что если вы это сделаете, вам нужно очень хорошо задокументировать, что им нужно делать, и надеяться, что они будут следовать вашим инструкциям. Там действительно нет способа обойти это. Вы также можете сделать своих трейдеров неизменными.

Идите к реализации «Клон» в обычном режиме и очень четко укажите в заголовке класса, что любой, унаследованный от вашего трейдера и добавляющий членов состояния, должен реализовать методы X (укажите, какие).

Поиск трюков, чтобы обойти фактическое Клонирование, и все же Клон не сможет преодолеть проблему наследования. Здесь нет серебряной пули.

2 голосов
/ 27 апреля 2009

Просто добавьте метод абстрактного копирования. Вы можете использовать ковариантные возвращаемые типы, чтобы указать производный тип для возврата производного экземпляра, что может быть или не быть важным.

public interface Trader {
    Trader copyTrader();
    ...
}


public final class MyTrader implements Trader {
    MyTrader copyTrader() {
        return new MyTrader(this);
    }
    ...
}

Иногда вам может понадобиться иметь дело с коллекцией производного типа Trader, которая должна клонироваться, и затем возвращать правильно типизированную коллекцию. Для этого вы можете использовать дженерики идиоматическим способом:

public interface Trader<THIS extends Trader> {
    THIS copyTrader();
    ...
}


public final class MyTrader implements Trader<MyTrader> {
    public MyTrader copyTrader() {
        return new MyTrader(this);
    }
    ...
}
0 голосов
/ 27 апреля 2009

Право Ури, полиморфизм с состоянием открывает большую банку с червями.

Я думаю, что создание подклассов Cloneable и переопределение clone (), вероятно, самый простой способ. Я полагаю, что вы можете сделать возвращаемый тип ковариантным.

0 голосов
/ 27 апреля 2009

Один из способов - сделать его конечным классом, таким как собственная строка Java, которая будет вносить любые изменения в объект класса Trader для создания новой копии в памяти, но сделает невозможным ее подкласс.

Другой (лучший) способ - использовать фабричный метод для создания и копирования объектов Trader, что подразумевает, что вы не должны разрешать использовать конструктор по умолчанию, то есть делать его закрытым. Таким образом, вы можете контролировать количество экземпляров класса. Смотри http://en.wikipedia.org/wiki/Factory_method

public class Trader {

    /* prevent constructor so new cant be used outside the factory method */
    private Trader() {
    }

    /* the factory method */
    public static Trader createTrader(int whatKindOfTrader) {

        switch (whatKindOfTrader) {
        case 0:
            return new Trader1(); // extends Trader
        case 1:
        default:
            return new Trader2(); // extends Trader
        }
        return new Trader3(); // or throw exception
    }
}

Вы можете даже указать другой перегруженный метод или второй аргумент, который берет одного трейдера и копирует его в новый, тем самым заменяя клон. Кстати, вы можете переопределить метод clone () и вызвать исключение CloneNotSupportedException, чтобы предотвратить клонирование объектов по умолчанию.

0 голосов
/ 27 апреля 2009

Один вариант: если вы можете сделать сериализуемые объекты, вы можете сериализовать их, а затем десериализовать, чтобы сделать копию, подобно тому, что происходит при передаче объекта через RMI.

Метод быстрого копирования:

public MyObject copy() {
    ObjectOutputStream oos = null;
    ObjectInputStream ois = null;
    try {
        ByteArrayOutputStream bos =  new ByteArrayOutputStream();
        oos = new ObjectOutputStream(bos);
        oos.writeObject(this);
        oos.flush();
        ByteArrayInputStream bin =
            new ByteArrayInputStream(bos.toByteArray());
        ois = new ObjectInputStream(bin);
        return (MyObject)ois.readObject();
    } catch(Exception e) {
        return null;
    } finally {
        try {
            oos.close();
            ois.close();
        } catch (Exception e) {
            return null;
        }
    }
}
...