Problem z klonowaniem obiektu.

0

Witam
Mam problem z klonowaniem obiektu w javie.

Fragment klasy ListaOsob:

public class ListaOsob implements Cloneable{
    List<Osoba> lista;

    public ListaOsob(){
        lista = new Vector<Osoba>();
    }

    @Override
    public ListaOsob clone(){
        try{
            ListaOsob nowa = (ListaOsob)super.clone();
            return nowa;
        }catch(CloneNotSupportedException e) {return null;}
    }

Osoba jest osobna klasa.

Probuje sklonowac obiekt o nazwie lista:
listaKopia = lista.clone();

Niestety gdy zmieniam jakies pole w obiekcie lista, zmiana zachodzi rowniez w obiekcie listaKopia

Z gory dziekuje za pomoc. Pozdrawiam.

0

Bo ta implementacja metody clone jest felerna :) Musisz wykonać głębokie klonowanie listy:


class Osoba implements Cloneable {

	@Override
	public Object clone() throws CloneNotSupportedException {
// różne warianty. Polecam jednak głębokie klonowanie
        }

}

class ListaOsob implements Cloneable {

	List<Osoba> lista;

	public ListaOsob() {
		lista = new Vector<Osoba>();
	}

	@Override
	public ListaOsob clone() {
		ListaOsob nowa = new ListaOsob();
		try {
			for (Osoba o : lista) {
				nowa.lista.add((Osoba) o.clone());
			}
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return nowa;
	}
}

Wykonane zostaje "głębokie konowanie", które powinno zostać też wykonane dla pól z klasy Osoba. Dzięki temu dostajesz nową, niezanieczyszczoną wersję obiektu.

0

Ponieważ w metodzie clone nie stworzyłeś nowej listy osób i referencja w nowym obiekcie odnosi się do starego obiektu. Aby ta metoda poniżej działała klasa Osoba też musi implementować metodę clone ;-)

@Override
public ListaOsob clone(){
	try{
		ListaOsob nowa = (ListaOsob)super.clone();
		nowa.lista = new Vector<Osoba>()
		for (Osoba osoba: lista) {
			nowa.lista.add((Osoba)osoba.clone());
		}
		return nowa;
	}catch(CloneNotSupportedException e) {
                throw new InternalError();
        }
}

EDIT: Koziołek mnie ubiegł ;-P

0

Wielkie dzieki!! Dziala:).

0

A ja mam taką ciekawostkę, chciałem sobie zrobić generyczną listę która by umożliwiała głębokie kopiowanie:

public class ClonList<V extends Cloneable> {
	private List<V> list;

	public ClonList() {
		list = new ArrayList<V>();
	}

	public void add(V v) {
		list.add(v);
	}

	@Override
	public Object clone() {
		try {
			ClonList<V> clon = new ClonList<V>();
			clon.list = new ArrayList<V>();
			for (V v : list) {
				clon.list.add((V) v.clone());
			}
			return clon;
		} catch (CloneNotSupportedException e) {
			throw new InternalError();
		}
	}
}

I co ciekawe nie da się bo ta linijka zwraca błąd:

clon.list.add((V) v.clone());

Znalazłem taką odpowiedź:
http://forums.sun.com/thread.jspa?threadID=5373687
Czy dało by się jakoś to ominąć :>

0

Ha... nadziałeś się na najbardziej buraczany błąd w API jaki jest, czyli protected dla metody clone(). Niestety nie można tego ominąć w prosty sposób. Jeżeli programujesz w czystej Javie bez takich fajerwerków jak GWT/DWR, czyli masz dostęp do refleksji to wystarczy, że wywołasz tą metodę za pomocą mechanizmu refleksji. Moja propozycja to ściągnięcie:
http://code.google.com/p/private-member-testing/ i zapoznanie się z metodą:
http://pmt.koziolekweb.pl/apidocs/pl/koziolekweb/pmt/PrivateMemberTester.html#runMethod%28java.lang.String,%20java.lang.Object,%20java.lang.Object[],%20java.lang.Class,%20java.lang.Class...%29

Jest to tylko jedna klasa, ale zrobiłem ją właśnie po to by uruchamiać metody na podstawie nazwy bez konieczności bezpośredniego grzebania w nich za pomocą refleksji.

0

Ciekawe rozwiązanie ;-) A czy czasami kiedyś metoda clone nie była publiczna ?

0

@kaziuuu, niestety z tego co pamiętam to nie (aż ściągnąłem api 1.1, by to sprawdzić). Zazwyczaj gdy ja nadpisujesz to zmieniasz na publiczną i dlatego jak już jej się używa to jako publicznej.

0

A jest mozliwe sklonowanie zwyklej listy obiektow, ktore nie sa "obudowane" o klase? Tak jakby lista osob nie byla klasa tylko zwykla ArrayLista przechowujaca osoby?

0

@riker, chodzi ci o coś w stylu:

ArrayList<T> newList =list.clone();

Czyli mamy na wyjściu nową listę, którą można swobodnie zmieniać nie zmieniając oryginalnej?
Hm...
Własna metoda przepisująca obiekty:

public List<?> rewrite(List<?> oryginalna){
   List<?> nowa = oryginalna.getClass().newInstance(); // taki sam typ listy
   for(Object o: oryginalna){
      nowa.add(o);
   }
   return nowa;
}

Coś w ten deseń.

0
Koziołek napisał(a)

@riker, chodzi ci o coś w stylu:

ArrayList<T> newList =list.clone();

Czyli mamy na wyjściu nową listę, którą można swobodnie zmieniać nie zmieniając oryginalnej?
Hm...
Własna metoda przepisująca obiekty:

public List<?> rewrite(List<?> oryginalna){
   List<?> nowa = oryginalna.getClass().newInstance(); // taki sam typ listy
   for(Object o: oryginalna){
      nowa.add(o);
   }
   return nowa;
}

Coś w ten deseń.

Sprytnie ;] Dzieki

0

Nie wiem, czy wiecie, ale jest darmowa biblioteka do prostego, głębokiego klonowania: http://robust-it.co.uk/clone/. Odkąd ją odkryłem wątpię, żeby jeszcze kiedykolwiek potrzebował/chciał bawić się w zawiłości ręcznego klonowania, bo faktycznie fatalnie rozwiązali tą sprawę w Javie. Oto przykład jej użycia:

import com.rits.cloning.Cloner;
...
private void pushState(){
	Cloner cloner = new Cloner();
	GameState stateClone = cloner.deepClone(currentState);
	stateStack.push(stateClone);
}

W ten sposób wykonujemy klon dowolnego obiektu, niezależnie od jego wewnętrznej konstrukcji. Są w tym api również inne metody, dające pełniejszą kontrolę nad przebiegiem klonowania. Ale szczerze mówiąc, nie miałem dotąd potrzeby się z nimi zapoznawać, bo najpowszechniejszy przypadek to właśnie głębokie klonowanie całego obiektu i od początku powinno to być w Javie tak proste jak z tą biblioteką ;).

0

No prościej niż w tej bibliotece się chyba już nie da [green]

1 użytkowników online, w tym zalogowanych: 0, gości: 1