2009-04-29 13 views
9

Dawno nie było klasy:Klonowanie z rodzajowych

public class Scope<C extends Cloneable & Comparable<C>> implements Comparable<Scope<C>>, Cloneable, Serializable { 

    private C starts; 
    private C ends; 
    ... 

    @SuppressWarnings("unchecked") 
    @Override 
    public Object clone() { 
     Scope<C> scope; 
     try { 
      scope = (Scope<C>) super.clone(); 
      scope.setStarts((C) starts.clone()); // The method clone() from the type Object is not visible 
      scope.setEnds((C) ends.clone()); // The method clone() from the type Object is not visible 
     } catch (CloneNotSupportedException e) { 
      throw new RuntimeException("Clone not supported"); 
     } 
     return scope; 
    } 
} 

w obiekcie mamy:

protected native Object clone() throws CloneNotSupportedException; 

I Cloneable interfejs jest:

public interface Cloneable { 
} 

Jak należy sklonować to?

+0

Nie bardzo rozumiem pytanie. Czy Scope nie ma już metody clone()? –

+2

Generics nie grają w ten problem. Co jeśli 'start' i' ends' to jakiś określony typ, który zaimplementował 'Cloneable', ale nie rozszerzył dostępu do" public ". Miałbyś ten sam problem. – erickson

Odpowiedz

10

Myślę, że obecna zielona odpowiedź jest zła, dlaczego możesz zapytać?

  • Dodaje dużo kodu
  • to wymaga, aby wymienić wszystkie pola mają być skopiowane i zrobić
  • to nie będzie działać na listach przy użyciu clone() (to co clone() dla HashMap mówi: Zwraca płytką kopię tej instancji HashMap: klucze i wartości są niesklonowane.), więc kończysz robienie tego ręcznie (to sprawia, że ​​płaczę)

Aha i na marginesie serializacja też jest zła, być może trzeba będzie dodać Serializable w każdym miejscu (to również sprawia, że ​​płaczę).

Więc jakie jest rozwiązanie:

Java Deep-Klonowanie bibliotekę Biblioteka klonowanie to niewielki, open source (licencja Apache) biblioteki Java, który głęboko klony obiekty. Obiekty nie muszą implementować interfejsu Cloneable. Skutecznie, ta biblioteka może sklonować dowolne obiekty Java. Może być używany, np. W implementacjach pamięci podręcznej, jeśli nie chcesz, aby obiekt buforowany był modyfikowany lub gdy chcesz utworzyć głęboką kopię obiektów.

Cloner cloner=new Cloner(); 
XX clone = cloner.deepClone(someObjectOfTypeXX); 

Sprawdź to na http://code.google.com/p/cloning/

+2

Semantycznie poprawna operacja klonowania musi zastąpić wszystkie pola, które zawierają stan zmiennego obiektu kopią tego obiektu, ale nie * musi * to robić z polami otaczającymi * tożsamość * obiektu (obiekty zawierające dowolne pola które zawierają zarówno zmienny stan, jak i tożsamość celu, nie mogą być poprawnie sklonowane w izolacji, może być możliwe klonowanie grupy takich obiektów, ale prawidłowe wykonanie wymaga zrozumienia relacji między nimi). Jeśli obiekt nigdy nie zostanie zmodyfikowany, niezależnie od tego, czy typ jest niezmienny, czy nie, głębokie klonowanie ... – supercat

+2

... będzie marnotrawstwem, ale (o ile tożsamość obiektu nie jest ważna) nie wpłynie na poprawność.Nie jestem do końca pewien, jak przydatne może być ogólne narzędzie do klonowania, chyba że jest używane z klasami, które oznaczają pola, które wymagają kopiowania, ponieważ potrzeba głębokiego klonowania pól jest funkcją sposobu użycia pól, oraz nie typ. – supercat

+0

Stan a tożsamość ... To jest problem ... –

4

Jest to jeden z powodów, dla których no one likes Cloneable. Powinien to być interfejs znacznika, ale w zasadzie jest bezużyteczny, ponieważ nie można sklonować dowolnego obiektu bez refleksji.

Praktycznie jedynym sposobem, aby to zrobić, aby stworzyć swój własny interfejs z publicznejclone() metody (nie muszą być nazywane „clone()”). Here's an example z innego pytania StackOverflow.

+0

Określa zachowanie implementacyjne (niezbyt dobre). Oddzielenie tych śmieci od interfejsu metod prawdopodobnie nie jest złym pomysłem. –

-1

Jak widać, jeśli klasa stara się wdrożyć Cloneable i chcesz głęboko klon, a następnie wszystkich składowych obiektów musi być niezmienne, prymitywne, lub muszą być również Cloneable.

Często lepszym i łatwiejszym podejściem jest tworzenie konstruktora kopii.

public class Scope<C extends Comparable<C>> implements Comparable<Scope<C>>, Serializable { 
    private C starts; 
    private C ends; 
    public Scope(final Scope original) { 
     starts = new C(original.starts); 
     ends = new C(original.ends); 
     // initialize all my other fields from "original" 
    } 
} 

i oczywiście trzeba konstruktor kopiujący na C, który jest w stanie obsługiwać polimorfizm.

Jeśli nie masz dostępu ani możliwości zmodyfikowania źródła na C, dowolna metoda kopiowania, bez względu na metodę, będzie bardzo trudna i potencjalnie niemożliwa. Na przykład nie można utworzyć kopii instancji enum.

+0

Tak, ale pytanie brzmi, jak sklonować ogólny parametr. Nie ma sposobu, aby określić, że typ musi mieć określony konstruktor. –

+0

Kiedy to zrobię, będę miał 2 odniesienia do tego samego obiektu startowego ... Czy nie? – Etam

+0

@etam: Oczywiście, że nie. Dlatego nazywa się to konstruktem * copy *. – Eddie

2

Lekko OT, ale można zaoszczędzić dużo przyszłego żalu z tego:

catch (CloneNotSupportedException e) { 
     throw new RuntimeException("Clone not supported", e); 
    } 

Tak, że gdy pojawi się ślad stosu znasz których przedmiotem jest przyczyną problemu.

Aby odpowiedzieć na podstawowe pytanie, twój własny interfejs, który implementuje publiczny klon jako mmyers napisał i wymaga, żeby C także to rozszerzył.

+0

To nie jest dobry pomysł. Chciałbym użyć na przykład daty. Nie przedłużam każdej klasy, z której korzystam. – Etam

+0

Jeśli chcesz tego dla typów, nad którymi nie masz kontroli, odbicie jest jedyną opcją. – Yishai

1

Jako ogólny komentarz, należy unikać używania Object.clone(), gdy tylko jest to możliwe. Jeśli masz kontrolę nad danym kodem, zaimplementuj konstruktor kopii. Aby uzyskać więcej informacji, patrz here.

+0

+1. To co powiedziałem. – Eddie

+1

Tak, ale nie ma możliwości użycia interfejsu do określenia konstruktora kopiowania. Oznacza to, że musisz znać typ obiektu, który chcesz skopiować. –

+1

@mmyers: Sort of true; alternatywą jest refleksja. Jednak w Cloneable masz inne ograniczenia, które mogą być równie trudne. – Eddie

2

Mam nadzieję, że już rozwiązany problem generycznego klonowania w Javie:

public class Generic<T> { 
    private T data; 

    public Generic() { 
    // ... 
    } 

    @SuppressWarnings("unchecked") 
    @Override 
    public Object clone() { 
    Generic<T> cloned = new Generic<T>(); 
    try { 
     cloned.data = (T) data.getClass().getMethod("clone").invoke(data); 
    } catch (Exception e) { 
     // ... 
    } 
    return cloned; 
    } 
}