Zastanawiam się, czy istnieje zalecany sposób wykonywania głębokiego klonowania / kopiowania instancji w Javie.
Mam na myśli 3 rozwiązania, ale niektórych mogę przegapić i chciałbym poznać Twoją opinię
edytuj: dołącz propositon Bohzo i doprecyzuj pytanie: bardziej chodzi o głębokie klonowanie niż płytkie klonowanie.
Zrób to sam:
zakoduj clone ręcznie właściwości po właściwościach i sprawdź, czy mutowalne instancje również są klonowane.
pro:
- kontrola tego, co zostanie wykonane
-
wady szybkiego wykonania :
- żmudne pisanie i konserwacja
- podatne na błędy (błąd kopiowania / wklejania, brakująca właściwość, ponownie przypisana zmienna właściwość)
Użyj odbicia:
Z własnymi narzędziami do refleksji lub z zewnętrznym pomocnikiem (takim jak fasolka dżakarta) łatwo jest napisać ogólną metodę kopiowania, która wykona zadanie w jednej linii.
pro:
- łatwy do napisania
- brak konserwacji
wady:
- mniejsza kontrola nad tym, co się dzieje
- podatne na błędy w przypadku obiektów mutowalnych, jeśli narzędzie odbicia nie klonuje również obiektów podrzędnych
- wolniejsze wykonywanie
Użyj struktury klonowania:
Użyj frameworka, który zrobi to za Ciebie, takiego jak:
commons-lang SerializationUtils
Java Deep Cloning Library
Dozer
Kryo
pro:
- to samo, co odbicie
- większa kontrola nad tym, co dokładnie zostanie sklonowane.
minusy:
- każda zmienna instancja jest w pełni sklonowana, nawet na końcu hierarchii
- może być bardzo powolna do wykonania
Użyj instrumentacji kodu bajtowego, aby napisać klon w czasie wykonywania
javassit , BCEL lub cglib mogą być użyte do wygenerowania dedykowanego klonera tak szybko, jak napisane jedną ręką. Ktoś zna bibliotekę używającą do tego celu jednego z tych narzędzi?
Co tu przegapiłem?
Który byś polecił?
Dzięki.
Odpowiedzi:
W przypadku głębokiego klonowania (klonuje całą hierarchię obiektów):
commons-lang SerializationUtils - używając serializacji - jeśli kontrolujesz wszystkie klasy i możesz wymusić implementację
Serializable
.Java Deep Cloning Library - przy użyciu odbicia - w przypadkach, gdy klasy lub obiekty, które chcesz sklonować, są poza Twoją kontrolą (biblioteka innej firmy) i nie możesz ich zaimplementować
Serializable
lub w przypadkach, gdy nie chcesz implementowaćSerializable
.W przypadku płytkiego klonowania (klonuje tylko właściwości pierwszego poziomu):
commons-beanutils BeanUtils - w większości przypadków.
Spring BeanUtils - jeśli używasz już spring i masz to narzędzie na ścieżce klas.
Celowo pominąłem opcję „zrób to sam” - powyższe API zapewnia dobrą kontrolę nad tym, co klonować, a czego nie (na przykład używając
transient
, lubString[] ignoreProperties
), więc ponowne wynalezienie koła nie jest preferowane.źródło
Książka Joshuy Blocha zawiera cały rozdział zatytułowany „Punkt 10: Zastąp klonowanie rozsądnie”, w którym wyjaśnia, dlaczego zastępowanie klonu w większości jest złym pomysłem, ponieważ specyfikacja Javy stwarza wiele problemów.
Zapewnia kilka alternatyw:
Użyj wzorca fabrycznego zamiast konstruktora:
Użyj konstruktora kopiującego:
Wszystkie klasy kolekcji w Javie obsługują konstruktor kopiujący (np. New ArrayList (l);)
źródło
Copyable
interfejs zawierającygetCopy()
metodę. Po prostu użyj ręcznie wzoru prototypu.newInstance()
metoda iYum
konstruktor zrobiłby głęboką kopię lub płytką kopię?Od wersji 2.07 Kryo obsługuje płytkie / głębokie klonowanie :
Kryo jest szybki, na końcu ich strony można znaleźć listę firm, które używają go w produkcji.
źródło
Użyj XStream toXML / fromXML w pamięci. Niezwykle szybki, istnieje już od dłuższego czasu i rośnie w siłę. Obiekty nie muszą być serializowane i nie musisz używać odbicia (chociaż XStream to robi). XStream potrafi rozróżniać zmienne wskazujące na ten sam obiekt i nie przypadkowo tworzy dwie pełne kopie instancji. Wiele takich szczegółów zostało dopracowanych przez lata. Używam go od wielu lat i jest to dobry pomysł. Jest tak łatwy w użyciu, jak możesz sobie wyobrazić.
lub
Klonować,
Bardziej zwięźle:
źródło
W przypadku skomplikowanych obiektów i gdy wydajność nie jest znacząca, używam gson do serializacji obiektu do tekstu JSON , a następnie deserializacji tekstu, aby uzyskać nowy obiekt.
gson, który oparty na odbiciu będzie działał w większości przypadków, z wyjątkiem tego, że
transient
pola nie zostaną skopiowane, a obiekty z cyklicznym odwołaniem z przyczynąStackOverflowError
.źródło
Zależy.
Aby uzyskać szybkość, użyj DIY. Aby uzyskać kuloodporność, użyj odbicia.
BTW, serializacja to nie to samo co refl, ponieważ niektóre obiekty mogą zapewniać nadpisane metody serializacji (readObject / writeObject) i mogą być wadliwe
źródło
Poleciłbym metodę DIY, która w połączeniu z dobrą metodą hashCode () i equals () powinna być łatwa do sprawdzenia w teście jednostkowym.
źródło
Sugerowałbym nadpisanie Object.clone (), najpierw wywołanie super.clone (), a następnie wywołanie ref = ref.clone () na wszystkich odniesieniach, które chcesz głęboko skopiować. To mniej więcej podejście Zrób to sam, ale wymaga trochę mniej kodowania.
źródło
W przypadku głębokiego klonowania zaimplementuj Serializable na każdej klasie, którą chcesz sklonować w ten sposób
A następnie użyj tej funkcji:
lubię to:
Obj newObject = (Obj)deepClone(oldObject);
źródło