Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Java: Общие вопросы > Как правильно реализовать clone()


Автор: Dims 3.12.2007, 20:02
Правильно ли я понимаю, что в общем случае mutable класс желательно должен иметь реализованный метод clone(), чтобы его объекты можно было копировать?

Я хочу сделать несколько классов, каждый из который представлял бы собой составной индекс, то есть, был бы массивом целых чисел. Этот индекс должен будет иметь метод inc, который "увеличивал бы индекс на единицу", то есть, переходил бы к следующему индексу по определённым правилам. 

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

Я определил такой абстрактный класс

Код

public abstract class CompoundIndex implements Cloneable {
    
    protected int[] index;
    
    public CompoundIndex(int length) {
        index = new int[ length ];
    }
    
    public int length() {
        return index.length;
    }
}


Как мне теперь реализовать метод clone()? Можно ли ограничиться реализацией в этом классе так, чтобы в наследниках работало правильно? Должен ли я использовать super.clone() при реализации?

Автор: Dims 3.12.2007, 20:32
Вот так правильно?

Код

    public Object clone() throws CloneNotSupportedException {
        CompoundIndex ans = (CompoundIndex) super.clone();
        ans.index = index.clone();
        return ans;
    }

Автор: Platon 3.12.2007, 21:55
На самом деле интересный вопрос...
IDEA подсвечивает с замечанием, что не вызван клон Object'а...
Я эе на этом не заморачиваюсь, просто копирую все поля объекта.

Автор: sith 3.12.2007, 22:04
... ты же можешь сам и проверить... написав простой JUnit  тест... ты ведь знаешь что должно получиться в результате твоего клонирования... сразу же и увидешь что имешь после выполнения этого кода

Автор: LSD 3.12.2007, 23:47
Цитата(Dims @  3.12.2007,  20:32 Найти цитируемый пост)
Вот так правильно?

Да. При желании можно еще поставить try/catch и убрать CloneNotSupportedException.


Цитата(Platon @  3.12.2007,  21:55 Найти цитируемый пост)
Я эе на этом не заморачиваюсь, просто копирую все поля объекта.

А каким макаром ты создаешь экземпляр объекта? Конструктором, что-ли? smile 

Автор: Platon 4.12.2007, 10:39
LSD, *CONFUSED*
Нууууу, да. *SHOOT_HIMSELF*
Ок, обращусь-ка я к докам за этим вопросом...

Автор: LSD 4.12.2007, 11:37
Цитата(Platon @  4.12.2007,  10:39 Найти цитируемый пост)
Нууууу, да.

А теперь представь, что кто-то унаследуется от твоего класса.

Автор: Dims 4.12.2007, 11:43
А в моём случае в наследниках переопределять clone() уже не будет нужно, как я понимаю. Правильный тип будет обеспечиваться сделанным вызовом super.

Ясно. А если перехватывать исключение, то что делать в обработчике? В каких случаях может возникнуть исключение? Казалось бы, во всех наследниках метод должен работать правильно и исключение не может возникнуть никогда?

А мне надо наследникам явно указывать implements Cloneable или они уже все по умолчанию унаследуют это свойство?

Автор: sith 4.12.2007, 11:46


Цитата(Dims @  4.12.2007,  11:43 Найти цитируемый пост)
А мне надо наследникам явно указывать implements Cloneable или они уже все по умолчанию унаследуют это свойство?


... должны унаследовать... 

Автор: LSD 4.12.2007, 11:53
Цитата(Dims @  4.12.2007,  11:43 Найти цитируемый пост)
А в моём случае в наследниках переопределять clone() уже не будет нужно, как я понимаю. Правильный тип будет обеспечиваться сделанным вызовом super.

Переопределять нужно будет если появятся новые поля и их тоже нужно будет клонировать (не все поля нужно клонировать, не изменяемые объекты клонировать не нужно).


Цитата(Dims @  4.12.2007,  11:43 Найти цитируемый пост)
Ясно. А если перехватывать исключение, то что делать в обработчике? В каких случаях может возникнуть исключение? Казалось бы, во всех наследниках метод должен работать правильно и исключение не может возникнуть никогда?

Оно может возникнуть, если класс не реализует интерфейс Cloneable, т.е. в твоем случае возникнуть не должно.

Автор: Platon 4.12.2007, 16:14
LSD, слава Богу, я клонированием направо и налево не занимаюсь (более того, в России на него мораторий), но в те скудных случаях когда делал его, проблем не возникало.

Автор: ressac 22.3.2008, 22:25
Цитата(Dims @ 3.12.2007,  18:32)
Вот так правильно?

Код

    public Object clone() throws CloneNotSupportedException {
        CompoundIndex ans = (CompoundIndex) super.clone();
        ans.index = index.clone();
        return ans;
    }

Я не понимаю почему мы должны ставить возвращаемый тип класса "Object"?  
А не "CompoundIndex"? 
Ведь мы приводим super.clone(); к CompoundIndex ?

и если честно не вижу разницу между КЛОНИРОВАНИЕ через метод clone() или просто копировать поля через конструктор?


Автор: w1nd 23.3.2008, 17:59
Цитата(ressac @  22.3.2008,  22:25 Найти цитируемый пост)
и если честно не вижу разницу между КЛОНИРОВАНИЕ через метод clone() или просто копировать поля через конструктор?

Разница в том, что Object.clone() клонирует любого наследника вашего класса полностью.

Цитата(LSD @  4.12.2007,  11:37 Найти цитируемый пост)
А теперь представь, что кто-то унаследуется от твоего класса.

Будет такая же дрянь, как если бы этот кто-то унаследовался бы от класса, в котором переопределён equals() и hashCode(). Штатная дрянь, так сказать. 

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


Автор: ressac 23.3.2008, 18:36
w1nd, не много понял из выше сказаного :( 

я яву только неделю учу, можно более доступным языком?

Автор: w1nd 23.3.2008, 23:13
Цитата(ressac @  23.3.2008,  18:36 Найти цитируемый пост)
w1nd, не много понял из выше сказаного :( я яву только неделю учу, можно более доступным языком?

Код
public static class A implements Cloneable {
        
    int i;
        
    public Object clone() {
        try {
            return super.clone();
        } catch (CloneNotSupportedException thrown) {
            throw new RuntimeException(thrown.getMessage(), thrown);
        }
    }
        
}
    
public static class B extends A {
        
    int j;
        
}

Вызов B.clone() скопирует и поля, объявленные в классе B.

Автор: ressac 23.3.2008, 23:21
w1nd, 
млин smile 
всё вышел из ступора smile 
спасибо большое, спать могу теперь идти спокойно smile

Автор: LSD 24.3.2008, 13:05
Цитата(w1nd @  23.3.2008,  17:59 Найти цитируемый пост)
Я всегда реализую clone() без использования Object.clone(), а тот, кто делает наследников, должен соображать чуть-чуть.

А вот с этого момента поподробней, как надо реализовывать в наследниках clone() если в суперклассе он реализован без использования Object.clone()?

Автор: w1nd 24.3.2008, 22:18
Цитата(LSD @  24.3.2008,  13:05 Найти цитируемый пост)
А вот с этого момента поподробней, как надо реализовывать в наследниках clone() если в суперклассе он реализован без использования Object.clone()?

Своим конструктором.

Автор: LSD 24.3.2008, 23:18
Цитата(w1nd @  24.3.2008,  22:18 Найти цитируемый пост)
Своим конструктором.

Это понятно. Как быть уверенным, что приватные поля суперклассов инициализированны корректно?

Автор: w1nd 24.3.2008, 23:41
Цитата(LSD @  24.3.2008,  23:18 Найти цитируемый пост)
Как быть уверенным, что приватные поля суперклассов инициализированны корректно?

Уверенным быть можно, если известна реализация (или имеются соглашения на этот счёт). В таком случае  я не вижу способа так реализовать clone() в наследнике, чтобы унаследованный конструктор суперкласса не работал. Разве что самому что-то напортачить:
Код
public class A implements Cloneable {
    
    int i;
    
    public A(int i) {
        this.i = i;
    }
    
    public A(A a) {
        i = a.i;
    }
    
    public A clone() {
        return new A(this);
    }
    
}

public class B extends A {
    
    int j;
    
    public B(int i, int j) {
        super(i);
        this.j = j;
    }
    
    public B(B b) {
        super(b);
        j = b.j;
    }
    
    public B clone() {
        return new B(this);
    }
    
}

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

Так же нельзя быть полностью уверенным в корректной инициализации класса при использовании Object.clone(). Например, такой класс может создать проблемы наследникам:
Код
public class C implements Cloneable {
    
    int i;
    
    public C(int i) {
        this.i = i;
        addMeInSomeRegistry(this);
    }
    
    public C clone() {
        try {
            return (C) super.clone();
        } catch (CloneNotSupportedException thrown) {
            throw new RuntimeException(thrown);
        }
    }
    
}

Речь не о том, что такая реализация не совсем верна, а о том, что таких реализаций - море. Поэтому предпочитаю насаждать более простую/понятную/надёжную схему. К тому же, она значительно быстрее работает smile

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

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)