Java klonlanabilir hakkında


96

Java'yı açıklayan bazı öğreticiler arıyordum Cloneable, ancak iyi bağlantılar alamadım ve Stack Overflow zaten daha bariz bir seçim haline geliyor.

Aşağıdakileri bilmek istiyorum:

  1. CloneableCloneablearayüzü uygulayarak nesnelerin bir kopyasına veya bir kopyasına sahip olabileceğimiz anlamına gelir . Bunu yapmanın avantajları ve dezavantajları nelerdir?
  2. Nesne bileşik bir nesne ise yinelemeli klonlama nasıl gerçekleşir?

2
Neye göre avantajları ve dezavantajları?
Galactus

4
Bunu, bir sınıfın Klonlanabilir olmasına karşı Klonlanabilir olmasının avantajı anlamına gelmek için okudum. Bunun başka nasıl yorumlanabileceğinden emin değilim: S
allyourcode

Yanıtlar:


159

Bilmeniz gereken ilk şey Cloneable, onu kullanmayın.

Klonlamayı Cloneabledoğru uygulamak çok zordur ve çabaya değmez.

Bunun yerine apache-commons SerializationUtils(derin klon) veya BeanUtils(sığ-klon) gibi diğer seçenekleri kullanın veya sadece bir kopya-yapıcı kullanın.

CloneableYaklaşımın birçok dezavantajını açıklayan Josh Bloch'un klonlama hakkındaki görüşleri için buraya bakın . ( Joshua Bloch bir Sun çalışanıydı ve çok sayıda Java özelliğinin geliştirilmesine öncülük etti.)


1
Bloch'un sözlerini birbirine bağladım (alıntı yapmak yerine)
Bozho

3
Block'un Cloneable'ı kullanmamayı söylediğini unutmayın. Klonlama kullanmayın demiyor (ya da en azından umarım). Yansımayı kullanan SerializationUtils veya BeanUtils gibi sınıflardan çok daha verimli olan klonlamayı uygulamanın birçok yolu vardır. Örnek için aşağıdaki yazıma bakın.
Charles

arabirimleri tanımlarken kopya oluşturucunun alternatifi nedir? basitçe bir kopyalama yöntemi mi eklemek istiyorsunuz?
benez

@benez evet derdim. Java-8'den beri staticarayüzlerde yöntemlere sahip olabilirsiniz , bu nedenle sadece bir static WhatEverTheInterface copy(WhatEverTheInterface initial)? ama klonlarken bir nesneden alanları kopyaladığınız için bunun size ne verdiğini merak ediyorum, ancak bir arayüz yalnızca yöntemleri tanımlar. açıklama ister misin?
Eugene

40

Cloneable'ın kendisi maalesef sadece bir işaret arabirimidir, yani: clone () yöntemini tanımlamaz.

Yaptığı şey, korumalı Object.clone () yönteminin davranışını değiştirmektir; bu, Cloneable uygulamayan sınıflar için bir CloneNotSupportedException oluşturur ve bunu yapan sınıflar için üye bazında yüzeysel bir kopya gerçekleştirir.

Aradığınız davranış bu olsa bile, bunu herkese açık hale getirmek için yine de kendi clone () yönteminizi uygulamanız gerekir.

Kendi clone () 'nizi uygularken, fikir, doğru sınıfta olduğu garanti edilen super.clone () ile nesne oluşturarak başlamak ve daha sonra sığ bir kopya değilse herhangi bir ek alan popülasyonu yapmaktır. İstediğiniz. Bir alt sınıfın kendi ek klonlanabilir mantığını eklemek istemesi durumunda kalıtımı bozacağından, clone () 'dan bir kurucu çağırmak sorunlu olacaktır; super.clone () çağrılırsa, bu durumda yanlış sınıftan bir nesne alırdı.

Bu yaklaşım, kurucularınızda tanımlanabilecek, potansiyel olarak sorunlu olabilecek herhangi bir mantığı atlar.

Diğer bir sorun da, clone () 'yu geçersiz kılmayı unutan tüm alt sınıfların otomatik olarak varsayılan yüzeysel kopyayı devralmasıdır; bu, değiştirilebilir durum durumunda muhtemelen istediğiniz şey değildir (şimdi kaynak ve kopya arasında paylaşılacaktır).

Çoğu geliştirici bu nedenlerle Cloneable'ı kullanmaz ve bunun yerine bir kopya oluşturucu uygular.

Cloneable hakkında daha fazla bilgi ve potansiyel tuzaklar için Joshua Bloch'un Effective Java kitabını şiddetle tavsiye ediyorum.


12
  1. Klonlama, yapıcılar olmadan nesneler oluşturmanın dil dışı bir yolunu çağırır.
  2. Klonlama, bir şekilde CloneNotSupportedException ile tedavi etmenizi ya da onu tedavi etmek için istemci kodunu rahatsız etmenizi gerektirir.
  3. Avantajlar azdır - sadece bir kopyalama yapıcısını manuel olarak yazmak zorunda değilsiniz.

Öyleyse, Cloneable'ı akıllıca kullanın. Her şeyi doğru yapmak için başvurmanız gereken çabaya kıyasla size yeterli fayda sağlamaz.


Bozho'nun dediği gibi Cloneable'ı kullanmayın. Bunun yerine, bir kopya yapıcı kullanın. Id=12
Bane

@Bane, klonlanacak nesnenin türünü bilmiyorsan, hangi sınıfın kopya oluşturucusunu çağıracağını nasıl bilebilirsin?
Steve Kuo

@Steve: Takip etmiyorum. Bir nesneyi klonlayacaksanız, ne tür olduğunu zaten bildiğinizi varsayıyorum - sonuçta elinizde klonlamayı planladığınız nesne var. Ve nesnenizin kendine özgü türünü daha genel bir nesneye kaptırdığı bir durum varsa, onu basit bir 'örneğini' kullanarak değerlendiremez misiniz ???
Bane

4
@Bane: Tümü A türünden türetilmiş, belki 10 farklı türde nesnelerin bir listeniz olduğunu varsayalım. Her bir nesnenin türünü bilmiyorsunuz. Bu durumda instanceof kullanmak ÇOK kötü bir fikirdir. Başka bir tür eklerseniz, bunu her yaptığınızda başka bir test örneği eklemeniz gerekir. Peki ya türetilmiş sınıflar başka bir paketteyse erişemiyorsunuz bile? Klonlama yaygın bir modeldir. Evet, java uygulaması kötü, ancak bunun etrafında iyi çalışacak birçok yol var. Kopya oluşturucu, eşdeğer bir işlem değildir.
Charles

@Charles: Ayrıntılı bir örneğin yokluğunda ve bu tür bir problemle başa çıkma konusunda son zamanlarda deneyimsiz olduğum için Bloch'a ertelemem gerekecek. Madde 11. Uzun ve biraz zor okunuyor, ancak temelde "mümkün olduğunca kopyalanmaktan kaçının, kopya oluşturucular arkadaşınızdır" diyor.
Bane

7

Klonlama, temel bir programlama paradigmasıdır. Java'nın onu birçok yönden kötü uygulamış olabileceği gerçeği, klonlama ihtiyacını hiçbir şekilde azaltmaz. Ve, nasıl çalışırsanız çalışsın, sığ, derin, karışık, her neyse, işe yarayacak klonlamayı uygulamak kolaydır. Hatta işlev için klon adını kullanabilir ve isterseniz Cloneable'ı uygulayamazsınız.

B ve C'nin A'dan türetildiği A, B ve C sınıflarım olduğunu varsayalım. A tipi nesnelerin bir listesine aşağıdaki gibi sahipsem:

ArrayList<A> list1;

Şimdi, bu liste A, B veya C tipi nesneleri içerebilir. Nesnelerin ne tür olduğunu bilmiyorsunuz. Yani listeyi şu şekilde kopyalayamazsınız:

ArrayList<A> list2 = new ArrayList<A>();
for(A a : list1) {
    list2.add(new A(a));
}

Nesne aslında B veya C türündeyse, doğru kopyayı alamazsınız. Ve ya A soyutsa? Şimdi, bazı insanlar bunu önerdi:

ArrayList<A> list2 = new ArrayList<A>();
for(A a : list1) {
    if(a instanceof A) {
        list2.add(new A(a));
    } else if(a instanceof B) {
        list2.add(new B(a));
    } else if(a instanceof C) {
        list2.add(new C(a));
    }
}

Bu çok çok kötü bir fikir. Ya yeni bir türetilmiş tür eklerseniz? Ya B veya C başka bir paketteyse ve bu sınıfta bunlara erişiminiz yoksa?

Yapmak istediğiniz şey şudur:

ArrayList<A> list2 = new ArrayList<A>();
for(A a : list1) {
    list2.add(a.clone());
}

Birçok kişi klonun temel Java uygulamasının neden sorunlu olduğunu belirtti. Ancak bu şekilde kolayca aşılabilir:

A sınıfında:

public A clone() {
    return new A(this);
}

B sınıfında:

@Override
public B clone() {
    return new B(this);
}

C sınıfında:

@Override
public C clone() {
    return new C(this):
}

Klonlanabilir uygulamıyorum, sadece aynı işlev adını kullanıyorum. Bundan hoşlanmıyorsanız, başka bir isim verin.


Yorumunuza ayrı bir cevapla cevap verdikten sonra bunu gördüm; Şimdi nereye gittiğinizi görüyorum, ancak 2 şey: 1) OP özellikle Cloneable'ı kullanmayı sordu (jenerik klonlama kavramı hakkında değil) ve 2) burada bir kopya oluşturucuyu ayırt etmeye çalışırken saçları biraz bölüyorsunuz ve genel klonlama kavramı. Burada aktardığınız fikir geçerlidir, ancak kökeninde sadece bir kopya yapıcı kullanıyorsunuz. ;)
Bane

Buradaki yaklaşımınıza, kullanıcıyı doğrudan kopyalama yapıcısını çağırmaya zorlamak yerine bir A # copyMethod () dahil etme yaklaşımınıza katılıyorum demek istiyorum.
Bane

5

A) Bir kopya oluşturucuya göre klonun pek çok avantajı yoktur. Muhtemelen en büyüğü, tamamen aynı dinamik tipte yeni bir nesne yaratma yeteneğidir (beyan edilen türün klonlanabilir olduğu ve genel bir klon yöntemine sahip olduğu varsayılırsa).

B) Varsayılan klon sığ bir kopya oluşturur ve klon uygulamanız bunu değiştirmedikçe sığ bir kopya olarak kalacaktır. Bu zor olabilir, özellikle sınıfınızın son alanları varsa

Bozho haklı, klonu doğru yapmak zor olabilir. Bir kopya kurucu / fabrika çoğu ihtiyacı karşılayacaktır.


0

Cloneable'ın dezavantajları nelerdir?

Kopyaladığınız nesnenin kompozisyonu varsa klonlama çok tehlikelidir.Bu durumda aşağıdaki olası yan etkiyi düşünmeniz gerekir çünkü klon sığ bir kopya oluşturur:

Db ile ilgili manipülasyonu işlemek için bir nesneniz olduğunu varsayalım. Diyelim ki bu Connectionnesnenin özelliklerinden biri nesneye sahip .

Öyleyse biri, originalObjectyaratılan nesnenin klonunu oluşturduğunda, diyelim cloneObject. Buraya originalObjectve cloneObjectaynı başvuruyu tutmak Connectionnesne.

Diyelim ki nesneyi originalObjectkapatır Connection, bu yüzden artık cloneObjectçalışmayacaktır çünkü connectionnesne aralarında paylaşılmıştır ve tarafından fiili olarak kapatılmıştır originalObject.

Bir özellik olarak IOStream'e sahip bir nesneyi klonlamak istediğinizde de benzer bir sorun ortaya çıkabilir.

Nesne bileşik bir nesne ise yinelemeli klonlama nasıl gerçekleşir?

Cloneable, sığ kopyalama gerçekleştirir. Bunun anlamı, orijinal nesnenin ve klon nesnesinin verilerinin aynı referansa / belleğe işaret etmesidir. derin kopyalama durumunda ise, orijinal nesnenin belleğindeki veriler klon nesnesinin belleğine kopyalanır.


Son paragrafınız çok karışık. Cloneablekopyalama Object.cloneyapmaz , yapar. "Orijinal nesnenin belleğindeki veriler, klon nesnesinin belleğine kopyalanır" tam olarak ne Object.cloneyapar. Derin kopyalamayı tanımlamak için başvurulan nesnelerin belleğinden bahsetmeniz gerekir.
aioobe
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.