Mengapa metode clone () dilindungi di java.lang.Object?

112

Apa alasan spesifik yang clone()didefinisikan sebagai dilindungi di java.lang.Object?

Alex N.
sumber

Jawaban:

107

Fakta bahwa klon dilindungi sangat meragukan - seperti fakta bahwa clonemetode tersebut tidak dideklarasikan di Cloneableantarmuka.

Itu membuat metode ini sangat tidak berguna untuk mengambil salinan data karena Anda tidak dapat mengatakan :

if(a instanceof Cloneable) {
    copy = ((Cloneable) a).clone();
}

Saya pikir desain Cloneablesekarang sebagian besar dianggap sebagai kesalahan (kutipan di bawah). Saya biasanya ingin dapat membuat implementasi antarmuka Cloneabletetapi tidak harus membuat antarmukaCloneable (mirip dengan penggunaan Serializable). Ini tidak dapat dilakukan tanpa refleksi:

ISomething i = ...
if (i instanceof Cloneable) {
   //DAMN! I Need to know about ISomethingImpl! Unless...
   copy = (ISomething) i.getClass().getMethod("clone").invoke(i);
}

Kutipan Dari Java Efektif Josh Bloch :
"Antarmuka yang Dapat Dikloning dimaksudkan sebagai antarmuka mixin bagi objek untuk mengiklankan bahwa mereka mengizinkan kloning. Sayangnya gagal untuk memenuhi tujuan ini ... Ini adalah penggunaan antarmuka yang sangat tidak lazim dan bukan untuk ditiru ... Untuk mengimplementasikan antarmuka agar memiliki efek apa pun pada kelas, ia dan semua kelas supernya harus mematuhi protokol yang cukup kompleks, tidak dapat diterapkan, dan sebagian besar tidak berdokumen "

oxbow_lakes
sumber
2
Jika objek bukan Cloneable, maka Object's clone () akan memunculkan CloneNotSupportedException. Jadi Anda perlu Cloneable jika Anda akan memanggil super.clone () (menghasilkan Object.clone () dipanggil). Saya tidak melihat bagaimana suatu objek dapat diserialkan tanpa menerapkan Serializable.
Steve Kuo
1
"Saya pikir desain Cloneable sekarang sebagian besar dianggap sebagai kesalahan." [butuh rujukan]
Kevin Panko
Maaf - saya tidak menyiratkan itu. Saya hanya menyiratkan bahwa desain yang "baik" adalah tidak membuat antarmuka diperpanjang Serializable- terserah pada implementasi untuk memutuskan apakah akan diimplementasikan Serializable. Saya memperluas ini menjadi Cloneable- ini bukan sesuatu yang harus diperluas antarmuka - tetapi implementasi antarmuka gratis Cloneable. Masalahnya adalah, jika Anda memiliki parameter tipe antarmuka, Anda akan bertanya apakah itu dapat di-clone; tapi kemudian Anda tidak bisa benar-benar mengkloningnya!
oxbow_lakes
6
@ Kevin - Josh Bloch efektif pp45 Java. "Antarmuka yang Dapat Dikloning dimaksudkan sebagai antarmuka campuran untuk objek yang akan diiklankan bahwa mereka mengizinkan kloning. Sayangnya itu gagal untuk melayani tujuan ini"
oxbow_lakes
Juga di halaman yang sama: "Ini adalah penggunaan antarmuka yang sangat tidak biasa dan bukan yang akan ditiru" dan "Agar penerapan antarmuka memiliki efek apa pun pada kelas, ia dan semua kelas supernya harus mematuhi yang cukup kompleks, protokol yang tidak dapat dilaksanakan dan sebagian besar tidak terdokumentasi "
oxbow_lakes
30

Antarmuka Clonable hanyalah penanda yang mengatakan bahwa kelas dapat mendukung kloning. Metode ini dilindungi karena Anda tidak boleh memanggilnya pada objek, Anda dapat (dan harus) menimpanya sebagai publik.

Dari Matahari:

Di kelas Object, metode clone () dinyatakan dilindungi. Jika semua yang Anda lakukan adalah mengimplementasikan Cloneable, hanya subclass dan anggota dari paket yang sama yang dapat memanggil clone () pada objek. Untuk mengaktifkan kelas apa pun dalam paket apa pun untuk mengakses metode clone (), Anda harus menimpanya dan mendeklarasikannya ke publik, seperti yang dilakukan di bawah ini. (Ketika Anda mengganti sebuah metode, Anda bisa membuatnya kurang privat, tapi tidak lebih privat. Di sini, metode clone () yang dilindungi di Object sedang diganti sebagai metode publik.)

Bill K
sumber
Yang bagus, sampai Anda membawa antarmuka ke dalam campuran - coba dan coba untuk mengkloning implementasi yang tidak diketahuiSet
oxbow_lakes
@oxbow_lakes: tapi mungkin beberapa implementasi dari Set tidak dapat di-clone
newacct
3
Anda tidak dapat mengkloning apa pun yang tidak mengimplementasikan antarmuka Clonable - ini adalah penanda yang mengatakan "Kelas ini dapat digandakan dengan benar" - sangat mirip dengan antarmuka Serializable. Ngomong-ngomong, ada cara untuk mengkloning kelas melalui serialisasi yang bekerja dengan baik - google sesuatu seperti "java serialization clone" dan Anda mungkin akan menemukan beberapa cara untuk mendapatkan salinan mendalam dari objek Anda.
Bill K
4
Anda tidak dapat mengkloning apa pun yang tidak mengimplementasikan antarmuka Cloneable, tetapi hanya karena sesuatu mengimplementasikan antarmuka Cloneable tidak berarti Anda dapat mengkloningnya.
Michael Myers
1
@BuckCherry toString memiliki implementasi default, jika Anda menyebutnya sesuatu yang baik akan terjadi dan Anda akan mendapatkan kembali sebuah string. sama memiliki implementasi default (sama seperti ==). Clone tidak dapat memiliki implementasi default. Jika Anda memanggil klon pada objek yang belum menerapkannya, Anda tidak akan mendapatkan perilaku yang wajar. Kloning rumit dan tidak dapat benar-benar dilakukan secara otomatis (beberapa objek tanpa konstruktor default tidak mungkin untuk digandakan secara umum), jadi secara default mereka membuatnya sedikit lebih aman. Saya pikir meletakkannya di Object sama sekali, bagaimanapun, mungkin tidak perlu.
Bill K
7

clonedilindungi karena itu adalah sesuatu yang harus diganti sehingga khusus untuk kelas saat ini. Meskipun dimungkinkan untuk membuat clonemetode publik yang akan mengkloning objek apa pun, ini tidak akan sebagus metode yang ditulis khusus untuk kelas yang membutuhkannya.

Andrew Hare
sumber
tetapi mengapa harus dilindungi untuk itu?
Janusz
3
Itu dilindungi sehingga Anda tidak menggunakan satu dalam objek (itu hanya akan membuat pengecualian). Mereka ingin Anda menimpanya di kelas, lalu Anda membuatnya menjadi publik. (dijawab beberapa kali di bawah ini juga)
Bill K
1
Dan tidak berguna ketika mempertimbangkan antarmuka sesuai poin saya di bawah
oxbow_lakes
harus diganti tidak terlalu jelas maka itu harus abstrak dan kemudian tidak boleh di Kelas Objek, saya berusaha keras untuk memahami mengapa demikian.
Kumar Abhishek
4

Metode Clone tidak dapat langsung digunakan pada objek apa pun, itulah sebabnya metode ini dimaksudkan untuk diganti oleh subclass.

Tentu saja itu bisa publik dan hanya membuang pengecualian yang sesuai saat kloning tidak memungkinkan, tapi saya pikir itu akan menyesatkan.

Cara kloning diterapkan saat ini membuat Anda berpikir tentang mengapa Anda ingin menggunakan kloning, dan bagaimana Anda ingin objek Anda dikloning.

Silfverstrom
sumber
2

Ini dilindungi karena implementasi default melakukan salinan dangkal berdasarkan anggota dari semua bidang (termasuk pribadi), menghindari konstruktor . Ini bukanlah sesuatu yang mungkin didesain untuk ditangani oleh objek (misalnya, mungkin melacak instance objek yang dibuat dalam daftar bersama, atau yang serupa).

Untuk alasan yang sama, implementasi default clone()akan melempar jika objek yang dipanggil tidak diimplementasikan Cloneable. Ini adalah operasi yang berpotensi tidak aman dengan konsekuensi yang luas, dan oleh karena itu penulis kelas harus ikut serta secara eksplisit.

Pavel Minaev
sumber
Sebenarnya implementasi default (dalam objek) melontarkan pengecualian menurut dokumen ...
Bill K
2
Tidak, itu tidak hanya melempar. Dari JavaDoc-nya: "Metode kloning untuk kelas Objek melakukan operasi kloning tertentu. Pertama, jika kelas objek ini tidak mengimplementasikan antarmuka Cloneable, maka CloneNotSupportedException akan dilemparkan. Perhatikan bahwa semua larik dianggap menerapkan antarmuka Cloneable. Jika tidak, metode ini membuat instance baru dari kelas objek ini dan menginisialisasi semua bidangnya dengan persis konten bidang terkait objek ini, seolah-olah dengan tugas; konten bidang itu sendiri tidak digandakan. "
Pavel Minaev
2

Dari javadoc cloneable.

* By convention, classes that implement this interface (cloneable) should override 
* <tt>Object.clone</tt> (which is protected) with a public method.
* See {@link java.lang.Object#clone()} for details on overriding this
* method.

* Note that this interface does <i>not</i> contain the <tt>clone</tt> method.
* Therefore, it is not possible to clone an object merely by virtue of the
* fact that it implements this interface.  Even if the clone method is invoked
* reflectively, there is no guarantee that it will succeed.

Jadi Anda dapat memanggil klon pada setiap objek tetapi ini akan memberi Anda sebagian besar waktu bukan hasil yang Anda inginkan atau pengecualian. Tetapi hanya dianjurkan jika Anda menerapkan cloneable.

Janusz
sumber
2
Anda tidak dapat memanggil klon pada setiap Objek, karena itu dilindungi!
Pavel Minaev
2

IMHO sesederhana ini:

  • #clone tidak boleh dipanggil pada objek yang tidak dapat digandakan, oleh karena itu tidak dipublikasikan
  • #cloneharus dipanggil oleh subclass ob Objectyang mengimplementasikan Cloneable untuk mendapatkan salinan dangkal dari kelas yang tepat

Apa cakupan yang tepat untuk metode yang dapat dipanggil oleh subkelas, tetapi tidak oleh kelas lain?

Itu protected.

Kelas yang mengimplementasikan Cloneabletentu saja akan membuat metode ini menjadi publik sehingga bisa dipanggil dari kelas lain.

Michaela Maura Elschner
sumber
0

Metode Clone () memiliki pemeriksaan internal 'instance Cloneable or not'. Ini adalah bagaimana tim Java mungkin berpikir akan membatasi penggunaan metode clone () yang tidak tepat. Metode clone () dilindungi yaitu hanya diakses oleh subclass. Karena objek adalah kelas induk dari semua sub kelas, maka metode Clone () dapat digunakan oleh semua kelas secara tidak langsung jika kita tidak memiliki pemeriksaan di atas dari 'instance of Cloneable'. Inilah alasan mengapa tim Java mungkin berpikir untuk membatasi penggunaan clone () yang tidak tepat dengan memeriksa metode clone () 'is it instance of Cloneable'.

Karenanya kelas apa pun yang mengimplementasikan cloneable dapat menggunakan metode clone () dari kelas Object.

Juga karena dibuat dilindungi, ini tersedia hanya untuk sub kelas yang mengimplementasikan antarmuka yang dapat digandakan. Jika kita ingin membuatnya menjadi publik, metode ini harus diganti oleh sub kelas dengan implementasinya sendiri.

SARIKA
sumber
-2

Ya, masalah yang sama yang saya temui. Tetapi saya menyelesaikannya dengan menerapkan kode ini

public class Side implements Cloneable {
    public Side clone() {

        Side side = null;
        try {
            side = (Side) super.clone();
        } catch (CloneNotSupportedException e) {
            System.err.println(e);
        }
        return side;
    }
}

Seperti yang dikatakan sebelumnya.

fyhao
sumber
1
CloneNotSupportedException adalah contoh lain dari pengecualian yang dicentang yang harus dihapus centangnya (artinya, ini harus memperluas RuntimeException, bukan Exception). Meskipun metode clone () di kelas, Side mengimplementasikan Cloneable dan oleh karena itu tidak akan pernah memunculkan CloneNotSupportedException, Side.clone () harus tetap menangkap atau mendeklarasikan pengecualian. Ini hanya menambahkan noise penanganan exception yang berlebihan ke metode clone ().
Derek Mahar
-2

Yah, juga para pengembang matahari hanyalah manusia, dan mereka memang membuat kesalahan besar untuk menerapkan metode klon sebagai dilindungi, kesalahan yang sama saat mereka menerapkan metode klon yang tidak berfungsi di ArrayList! Jadi, secara umum, ada kesalahpahaman yang jauh lebih dalam dari programmer Java yang berpengalaman tentang metode klon.

Namun, saya baru-baru ini menemukan solusi cepat dan mudah untuk menyalin objek apapun dengan semua isinya, tidak peduli bagaimana itu dibuat dan apa isinya, lihat jawaban saya di sini: Bug dalam menggunakan Object.clone ()

menandai
sumber
-3

Sekali lagi, kerangka kerja Java JDK menunjukkan pemikiran yang brilian:

Antarmuka yang dapat digandakan tidak berisi "klon T publik ();" metode karena ia bertindak lebih seperti atribut (mis. Serializable) yang memungkinkan sebuah instance untuk dikloning.

Tidak ada yang salah dengan desain ini karena:

  1. Object.clone () tidak akan melakukan apa yang Anda inginkan dengan kelas yang ditentukan khusus Anda.

  2. Jika Anda memiliki Myclass mengimplementasikan Cloneable => Anda menimpa clone () dengan "public MyClass clone ()"

  3. Jika Anda memiliki MyInterface extends Cloneable dan beberapa MyClasses mengimplementasikan MyInterface: cukup definisikan "public MyInterface clone ();" di antarmuka dan setiap metode yang menggunakan objek MyInterface akan dapat mengkloningnya, apa pun kelas MyClass-nya.

Pemenang
sumber
2
jika kelas Anda kebetulan diwarisi, implementasi klon kelas turunan Anda tidak akan aman sampai implementasi kelas dasar Anda menyediakan metode klon yang aman. Juga, ini adalah jenis kondisi desain yang tidak biasa untuk memiliki antarmuka tanpa metode / atribut. Antarmuka ini tidak memaksa kelas untuk mengimplementasikan klon.
prap19