Java: bir örneği derin klonlama / kopyalama için önerilen çözüm


176

Java derin klon / örnek kopyası yapmak için önerilen bir yolu olup olmadığını merak ediyorum.

Aklımda 3 çözüm var, ancak bazılarını kaçırabilirim ve fikrinizi almak istiyorum

edit: Bohzo önerisi dahil ve soru rafine: bu sığ klonlama daha derin klonlama hakkında.

Kendin Yap:

özelliklerden sonra elle özellikleri klonlayın ve değişebilir örneklerin de klonlandığını kontrol edin.
pro:
- ne yapılacağını kontrol
- hızlı yürütme
eksilerini:
- yazma ve bakım için sıkıcı
- hata eğilimli (kopyalama / yapıştırma hatası, eksik özellik, yeniden atanmış değiştirilebilir özellik)

Yansımayı kullan:

Kendi yansıma araçlarınızla veya harici bir yardımcıyla (jakarta ortak fasulyeleri gibi) işi bir satırda yapacak genel bir kopyalama yöntemi yazmak kolaydır.
pro:
- kolay yazma
- bakım
eksilerini yok :
- ne olacağını daha az kontrol
- yansıma aracı alt nesneleri de klonlamazsa değiştirilebilir nesne ile eğilimli hata
- yavaş yürütme

Klon çerçevesini kullanın:

Bunu sizin için yapan bir çerçeve kullanın, örneğin:
commons-lang Serileştirme
Java Derin Klonlama Kütüphanesi
Dozer
Kryo'yu kullanır

pro:
- yansıma ile aynı
- tam olarak klonlanacaklar üzerinde daha fazla kontrol.
eksileri:
- her değişebilir örnek, hiyerarşinin sonunda bile tamamen klonlanır
- yürütmek çok yavaş olabilir

Çalışma zamanında klon yazmak için bayt kodu araçlarını kullanma

javassit , BCEL veya cglib , bir elin yazdığı kadar hızlı bir özel klon oluşturmak için kullanılabilir. Birisi bu amaç için bu araçlardan birini kullanan bir lib biliyor mu?

Burada ne kaçırdım?
Hangisini önerirsiniz ?

Teşekkürler.


1
Görünüşe göre Java Deep Cloning Library buraya taşındı: code.google.com/p/cloning
Mr_and_Mrs_D

Yanıtlar:


155

Derin klonlama için (tüm nesne hiyerarşisini klonlar):

  • commons-lang SerializationUtils - serileştirmeyi kullanarak - tüm sınıflar kontrolünüzdeyse ve uygulamayı zorlayabilirsiniz Serializable.

  • Java Deep Cloning Library - yansıma kullanarak - klonlamak istediğiniz sınıfların veya nesnelerin kontrolünüz dışında olduğu (3. taraf kitaplığı) ve bunları uygulayamazsanız Serializableveya uygulamak istemediğiniz durumlarda Serializable.

Sığ klonlama için (sadece ilk seviye özelliklerini klonlar):

"Kendin yap" seçeneğini kasıtlı olarak atladım - yukarıdaki API'ler, klonlanıp neyin kopyalanmayacağı (örneğin transient, veya kullanılarak String[] ignoreProperties) üzerinde iyi bir kontrol sağlar , bu nedenle tekerleği yeniden icat etmek tercih edilmez.


Teşekkürler Bozho, bu değerli. Ve DIY seçeneği hakkında sana katılıyorum! Müştereklerin serileştirmesini ve / veya derin klonlama lib'ini hiç denediniz mi? Perfler ne olacak?
Guillaume

Evet, yukarıdaki seçeneklerin tümünü kullandım, yukarıdaki nedenlerden dolayı :) sadece klonlama kütüphanesinin CGLIB proxy'leri söz konusu olduğunda bazı sorunları vardı ve istenen bazı işlevleri kaçırdı, ancak şimdi düzeltilmesi gerektiğini düşünüyorum.
Bozho

Hey, benim Varlığım bağlı ve tembel şeyler varsa, SerializationUtils tembel özellikleri için veritabanını denetler mi? Çünkü istediğim bu ve öyle değil!
Cosmin Cosmin

aktif bir oturumunuz varsa - evet, öyle.
Bozho

@Bozho Yani fasulye içindeki tüm nesneler serileştirilebilir mi, org.apache.commons.beanutils.BeanUtils.cloneBean (obj) derin bir kopya yapacak mı?
hop

36

Joshua Bloch'un kitabında "Öğe 10: Klonu Doğru Bir Şekilde Geçersiz Kıl" başlıklı bir bölüm vardır ve burada klonun çoğunlukla geçersiz kılmasının neden kötü bir fikir olduğuna girer, çünkü bunun için Java özelliği birçok sorun yaratır.

Birkaç alternatif sunuyor:

  • Yapıcı yerine bir fabrika deseni kullanın:

         public static Yum newInstance(Yum yum);
  • Bir kopya oluşturucu kullanın:

         public Yum(Yum yum);

Java'daki tüm koleksiyon sınıfları kopya oluşturucuyu destekler (örn. Yeni ArrayList (l);)


1
Kabul. Projemde yöntem Copyableiçeren bir arayüz tanımladım getCopy(). Sadece prototip desenini manuel olarak kullanın.
gpampara

Klonlanabilir arayüz hakkında değil, derin bir klon / kopyalama işleminin nasıl yapılacağını soruyordum. Bir kurucu veya fabrikada, yeni örneğinizi kaynağınızdan oluşturmanız gerekir.
Guillaume

@Guillaume Bence derin klon / kopya sözcüklerini kullanırken dikkatli olmalısınız. Java'daki klonlama ve kopyalama aynı anlama gelmez. Java spec bu konuda söylenecek daha çok şey var .... Ben söyleyebilirim derin bir kopya istiyorum düşünüyorum.
LeWoody

Tamam Java spec, bir klonun ne olduğu hakkında doğrudur ... Ancak klondan daha yaygın bir anlamda da konuşabiliriz ... Örneğin, bohzo tarafından önerilen liblerden birine 'Java Deep Cloning Library' denir ...
Guillaume

2
@LWoodyiii bu newInstance()yöntem ve Yumyapıcı derin kopya veya sığ kopya yapmak istiyorsunuz?
Geek


5

Bellekte XStream toXML / fromXML kullanın. Son derece hızlı ve uzun süredir var ve güçlü. Nesnelerin Serileştirilebilir olması gerekmez ve yansıma kullanmanız gerekmez (XStream olsa da). XStream, aynı nesneye işaret eden ve yanlışlıkla örneğin iki tam kopyasını oluşturmayan değişkenleri algılayabilir. Yıllar boyunca bunun gibi birçok detay ortaya çıkarıldı. Birkaç yıldır kullandım ve bu bir gitmek. Tahmin edebileceğiniz kadar kullanımı kolaydır.

new XStream().toXML(myObj)

veya

new XStream().fromXML(myXML)

Klonlama,

new XStream().fromXML(new XStream().toXML(myObj))

Daha özlü:

XStream x = new XStream();
Object myClone = x.fromXML(x.toXML(myObj));

3

Karmaşık nesneler için ve performans önemli olmadığında , json metnine nesneyi serileştirmek için gson'u kullanın, sonra yeni nesneyi almak için metnin serileştirmesini yapın.

transientalanlara kopyalanmayacak ve sebepli dairesel referanslı nesneler dışında yansımaya dayalı gson çoğu durumda çalışır StackOverflowError.

public static <ObjectType> ObjectType Copy(ObjectType AnObject, Class<ObjectType> ClassInfo)
{
    Gson gson = new GsonBuilder().create();
    String text = gson.toJson(AnObject);
    ObjectType newObject = gson.fromJson(text, ClassInfo);
    return newObject;
}
public static void main(String[] args)
{
    MyObject anObject ...
    MyObject copyObject = Copy(o, MyObject.class);

}

2

Bağlı olmak.

Hız için DIY kullanın. Kurşun geçirmezlik için yansıma kullanın.

BTW, serileştirme yansıma ile aynı değildir, çünkü bazı nesneler geçersiz kılınmış serileştirme yöntemleri (readObject / writeObject) sağlayabilir ve buggy olabilir


1
yansıma kurşun geçirmez değildir: klonlanmış nesnenizin kaynağınıza referans verdiği bazı durumlarda yol açabilir ... Kaynak değişirse, klon da değişecektir!
Guillaume

1

İyi bir hashCode () ve equals () yöntemiyle birlikte bir birim testinde kanıtlanması kolay olan DIY yolunu öneriyorum.


Şey, tembel bana böyle bir kukla kod oluştururken çok sıralıyor. Ama daha akıllı bir yola benziyor ...
Guillaume

2
üzgünüm, ama DIY gitmek yoludur SADECE başka hiçbir çözüm you..which için uygun olup olmadığını neredeyse hiçbir zaman
Bozho

1

Object.clone () geçersiz kılmayı, önce super.clone () 'i çağırıp daha sonra derin kopyalamak istediğiniz tüm başvurularda ref = ref.clone ()' i çağırmanızı öneririm. Az ya da çok kendiniz yapın ama biraz daha az kodlamaya ihtiyaç var.


2
Bu, (kırık) klon yönteminin birçok sorunundan biridir: Bir sınıf hiyerarşisinde her zaman kolayca unutulabilen super.clone () 'i çağırmanız gerekir, bu yüzden bir kopya oluşturucu kullanmayı tercih ederim.
helpermethod

0

Derin klonlama için, bu şekilde klonlamak istediğiniz her sınıfta Serializable uygulayın

public static class Obj implements Serializable {
    public int a, b;
    public Obj(int a, int b) {
        this.a = a;
        this.b = b;
    }
}

Ve sonra bu işlevi kullanın:

public static Object deepClone(Object object) {
    try {
        ByteArrayOutputStream baOs = new ByteArrayOutputStream();
        ObjectOutputStream oOs = new ObjectOutputStream(baOs);
        oOs.writeObject(object);
        ByteArrayInputStream baIs = new ByteArrayInputStream(baOs.toByteArray());
        ObjectInputStream oIs = new ObjectInputStream(baIs);
        return oIs.readObject();
    }
    catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}

bunun gibi: Obj newObject = (Obj)deepClone(oldObject);

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.