เหตุใดวิธีการ clone () จึงถูกป้องกันใน java.lang.Object


คำตอบ:


107

ความจริงที่ว่าโคลนได้รับการป้องกันนั้นเป็นเรื่องที่น่าสงสัยอย่างยิ่งเช่นเดียวกับความจริงที่ว่าcloneวิธีนี้ไม่ได้รับการประกาศในCloneableอินเทอร์เฟซ

ทำให้วิธีนี้ไม่มีประโยชน์สำหรับการคัดลอกข้อมูลเพราะคุณไม่สามารถพูดว่า :

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

ฉันคิดว่าCloneableตอนนี้การออกแบบส่วนใหญ่ถือเป็นความผิดพลาด (อ้างอิงด้านล่าง) โดยปกติฉันต้องการที่จะสามารถใช้งานอินเทอร์เฟซได้Cloneableแต่ไม่จำเป็นต้องสร้างอินเทอร์เฟซCloneable (คล้ายกับการใช้งานSerializable) สิ่งนี้ไม่สามารถทำได้หากไม่มีการไตร่ตรอง:

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

การอ้างอิงจากJava ที่มีประสิทธิภาพของ Josh Bloch :
"อินเทอร์เฟซ Cloneable มีไว้เพื่อใช้เป็นอินเทอร์เฟซแบบ mixin สำหรับอ็อบเจ็กต์เพื่อโฆษณาว่าอนุญาตให้มีการโคลนนิ่ง แต่น่าเสียดายที่ไม่สามารถตอบสนองวัตถุประสงค์นี้ได้ ... นี่เป็นการใช้อินเทอร์เฟซที่ผิดปกติอย่างมากและไม่มีการเลียนแบบ ... เพื่อให้การใช้งานอินเทอร์เฟซมีผลกับคลาสใดคลาสหนึ่งมันและคลาสซูเปอร์คลาสทั้งหมดจะต้องเป็นไปตามโปรโตคอลที่ค่อนข้างซับซ้อนไม่สามารถบังคับใช้ได้และส่วนใหญ่ไม่มีเอกสาร "


2
ถ้าวัตถุไม่สามารถโคลนได้โคลนของวัตถุ () จะโยน CloneNotSupportedException ดังนั้นคุณจะต้องสามารถโคลนได้หากคุณจะเรียก super.clone () (ส่งผลให้ Object.clone () ถูกเรียก) ฉันไม่เห็นว่าออบเจ็กต์สามารถต่ออนุกรมได้อย่างไรโดยไม่ต้องใช้ Serializable
Steve Kuo

1
"ฉันคิดว่าตอนนี้การออกแบบ Cloneable ส่วนใหญ่ถือเป็นความผิดพลาด" [ต้องการอ้างอิง]
Kevin Panko

ขอโทษ - ฉันไม่ได้บอกเป็นนัยว่า ฉันบอกเป็นนัยว่าการออกแบบที่ "ดี" คือการไม่ทำให้อินเทอร์เฟซขยายขึ้นSerializable- ขึ้นอยู่กับการนำไปใช้งานเพื่อตัดสินใจว่าจะใช้งานSerializableหรือไม่ ผมได้รับการขยายนี้Cloneable- มันไม่ได้เป็นสิ่งที่อินเตอร์เฟซที่ควรขยาย - Cloneableแต่การดำเนินการของอินเตอร์เฟซที่มีอิสระที่จะเป็น ปัญหาคือถ้าคุณมีพารามิเตอร์ของประเภทอินเทอร์เฟซคุณจะถามว่ามันสามารถโคลนได้หรือไม่ แต่แล้วคุณไม่สามารถโคลนได้จริง!
oxbow_lakes

6
@Kevin - Java pp45 ที่มีประสิทธิภาพของ Josh Bloch "อินเทอร์เฟซ Cloneable มีวัตถุประสงค์เพื่อเป็นอินเทอร์เฟซมิกซ์อินสำหรับอ็อบเจ็กต์เพื่อโฆษณาว่าอนุญาตให้มีการโคลนนิ่ง แต่น่าเสียดายที่ไม่สามารถตอบสนองวัตถุประสงค์นี้ได้"
oxbow_lakes

ในหน้าเดียวกัน: "นี่เป็นการใช้อินเทอร์เฟซที่ผิดปกติอย่างมากและไม่มีการเลียนแบบ"และ"เพื่อที่จะนำอินเทอร์เฟซไปใช้เพื่อให้มีผลกระทบใด ๆ กับคลาสนั้นและซูเปอร์คลาสทั้งหมดจะต้องเป็นไปตามขั้นตอนที่ค่อนข้างซับซ้อน โปรโตคอลที่ไม่สามารถบังคับใช้ได้และส่วนใหญ่ไม่มีเอกสาร "
oxbow_lakes

30

อินเทอร์เฟซ Clonable เป็นเพียงเครื่องหมายบอกว่าคลาสสามารถรองรับการโคลนได้ เมธอดนี้ได้รับการป้องกันเนื่องจากคุณไม่ควรเรียกมันบนวัตถุคุณสามารถ (และควร) แทนที่เป็นสาธารณะ

ตั้งแต่อาทิตย์:

ในคลาส Object จะมีการประกาศวิธีการ clone () ที่ได้รับการป้องกัน หากสิ่งที่คุณทำคือใช้ Cloneable เฉพาะคลาสย่อยและสมาชิกของแพ็กเกจเดียวกันเท่านั้นที่จะสามารถเรียกใช้ clone () บนอ็อบเจ็กต์ได้ ในการเปิดใช้งานคลาสใด ๆ ในแพ็คเกจใด ๆ เพื่อเข้าถึงวิธีการ clone () คุณจะต้องลบล้างและประกาศเป็นสาธารณะดังที่ทำด้านล่าง (เมื่อคุณลบล้างวิธีการคุณสามารถทำให้เป็นส่วนตัวน้อยลง แต่ไม่เป็นส่วนตัวมากขึ้นที่นี่เมธอด clone () ที่มีการป้องกันใน Object จะถูกแทนที่เป็นวิธีสาธารณะ)


ซึ่งใช้ได้ดีจนกว่าคุณจะนำอินเทอร์เฟซเข้ามาผสม - ลองพยายามโคลนการใช้งานที่ไม่รู้จักSet
oxbow_lakes

@oxbow_lakes: แต่การใช้งาน Set บางอย่างอาจไม่สามารถ
ลอกแบบได้

3
คุณไม่สามารถโคลนสิ่งใดก็ตามที่ไม่ใช้อินเทอร์เฟซ Clonable - เป็นเครื่องหมายที่ระบุว่า "คลาสนี้สามารถโคลนได้อย่างถูกต้อง" ซึ่งคล้ายกับอินเทอร์เฟซที่ต่ออนุกรมได้ อย่างไรก็ตามมีวิธีการโคลนคลาสผ่านการทำให้เป็นอนุกรมที่ใช้งานได้ดีนั่นคือ Google เช่น "java serialization clone" และคุณอาจพบหลายวิธีในการคัดลอกวัตถุของคุณในระดับลึก
Bill K

4
คุณไม่สามารถโคลนสิ่งที่ไม่ใช้อินเทอร์เฟซ Cloneable ได้ แต่เพียงเพราะมีบางอย่างที่ใช้อินเทอร์เฟซ Cloneable ไม่ได้หมายความว่าคุณสามารถโคลนได้
Michael Myers

1
@BuckCherry toString มีการใช้งานเริ่มต้นหากคุณเรียกมันว่าสิ่งดีๆจะเกิดขึ้นและคุณจะได้รับสตริงกลับมา เท่ากับมีการใช้งานเริ่มต้น (เช่นเดียวกับ ==) โคลนไม่สามารถใช้งานเริ่มต้นได้ หากคุณเรียกโคลนบนวัตถุที่ไม่ได้ใช้งานคุณจะไม่ได้รับพฤติกรรมที่สมเหตุสมผล การโคลนนิ่งมีความซับซ้อนและไม่สามารถทำได้โดยอัตโนมัติ (วัตถุบางอย่างที่ไม่มีตัวสร้างเริ่มต้นอาจเป็นไปไม่ได้ที่จะโคลนนิ่งโดยทั่วไป) ดังนั้นโดยค่าเริ่มต้นพวกเขาจะทำให้ปลอดภัยขึ้นเล็กน้อย ฉันคิดว่าการวางไว้บน Object เลยอาจไม่จำเป็น
Bill K

7

cloneได้รับการปกป้องเนื่องจากเป็นสิ่งที่ควรจะถูกแทนที่เพื่อให้เฉพาะกับคลาสปัจจุบัน แม้ว่าจะเป็นไปได้ที่จะสร้างcloneเมธอดสาธารณะที่จะโคลนอ็อบเจ็กต์ใด ๆ เลย แต่ก็ไม่ดีเท่ากับเมธอดที่เขียนขึ้นเฉพาะสำหรับคลาสที่ต้องการ


แต่เหตุใดจึงต้องได้รับการคุ้มครองเช่นนั้น?
Janusz

3
ได้รับการป้องกันดังนั้นคุณจึงไม่ใช้สิ่งที่อยู่ในวัตถุ (มันก็จะมีข้อยกเว้นอยู่ดี) พวกเขาต้องการให้คุณลบล้างในชั้นเรียนจากนั้นจึงกำหนดให้เป็นสาธารณะ (ตอบสองสามครั้งด้านล่างด้วย)
Bill K

1
และไร้ประโยชน์เมื่อพิจารณาอินเทอร์เฟซตามจุดของฉันด้านล่าง
oxbow_lakes

ควรจะถูกลบล้างไม่ชัดเจนนักดังนั้นควรเป็นนามธรรมและจากนั้นจะต้องไม่อยู่ใน Object Class ฉันพยายามอย่างยิ่งที่จะเข้าใจว่าเหตุใดจึงเป็นเช่นนั้น
Kumar Abhishek

4

ไม่สามารถใช้เมธอด Clone กับวัตถุใด ๆ ได้โดยตรงซึ่งเป็นสาเหตุที่ทำให้คลาสย่อยถูกลบล้าง

แน่นอนว่าอาจเป็นสาธารณะและเพียงแค่โยนข้อยกเว้นที่เหมาะสมเมื่อไม่สามารถทำการโคลนนิ่งได้ แต่ฉันคิดว่านั่นจะทำให้เข้าใจผิด

วิธีการใช้งานโคลนในตอนนี้ทำให้คุณคิดถึงสาเหตุที่คุณต้องการใช้โคลนและวิธีที่คุณต้องการให้วัตถุของคุณถูกโคลน


2

มันได้รับการคุ้มครองเพราะเริ่มต้นใช้งานไม่สำเนาตื้น memberwise ของทุกสาขา (รวมทั้งเอกชน) หลีกเลี่ยงคอนสตรัค นี่ไม่ใช่สิ่งที่ออบเจ็กต์อาจได้รับการออกแบบมาเพื่อจัดการตั้งแต่แรก (เช่นอาจติดตามอินสแตนซ์ออบเจ็กต์ที่สร้างขึ้นในรายการที่แชร์หรือสิ่งที่คล้ายกัน)

ด้วยเหตุผลเดียวกันที่เริ่มต้นใช้งานจะโยนถ้าวัตถุที่มันเรียกว่าไม่ใช้clone() Cloneableเป็นการดำเนินการที่อาจไม่ปลอดภัยและมีผลกระทบในวงกว้างดังนั้นผู้เขียนชั้นเรียนจึงต้องเลือกเข้าร่วมอย่างชัดเจน


ที่จริงแล้วการใช้งานเริ่มต้น (ในวัตถุ) มีข้อยกเว้นตามเอกสาร ...
Bill K

2
ไม่มันไม่เพียงแค่โยน จาก JavaDoc: "เมธอดโคลนสำหรับคลาสอ็อบเจ็กต์ดำเนินการโคลนเฉพาะขั้นแรกถ้าคลาสของอ็อบเจ็กต์นี้ไม่ใช้อินเทอร์เฟซ Cloneable ดังนั้น CloneNotSupportedException จะถูกโยนออกไปโปรดทราบว่าอาร์เรย์ทั้งหมดถือว่าใช้อินเทอร์เฟซ Cloneable มิฉะนั้นเมธอดนี้จะสร้างอินสแตนซ์ใหม่ของคลาสของอ็อบเจ็กต์นี้และเริ่มต้นฟิลด์ทั้งหมดโดยมีเนื้อหาของฟิลด์ที่เกี่ยวข้องของอ็อบเจ็กต์นี้เหมือนกับการกำหนดเนื้อหาของฟิลด์ไม่ได้ถูกโคลนนิ่งเอง "
Pavel Minaev

2

จาก 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.

ดังนั้นคุณสามารถเรียกโคลนบนวัตถุทุกชิ้นได้ แต่จะให้เวลาส่วนใหญ่ไม่ใช่ผลลัพธ์ที่คุณต้องการหรือเป็นข้อยกเว้น แต่จะได้รับการสนับสนุนเฉพาะในกรณีที่คุณใช้โคลนได้


2
คุณไม่สามารถเรียกโคลนบนวัตถุทุกชิ้นได้เพราะมันถูกป้องกัน!
Pavel Minaev

2

IMHO ง่ายเพียงเท่านี้:

  • #clone จะต้องไม่ถูกเรียกใช้กับวัตถุที่ไม่สามารถโคลนได้ดังนั้นจึงไม่เปิดเผยต่อสาธารณะ
  • #cloneจะต้องถูกเรียกโดยคลาสย่อย ob Objectที่ใช้ Cloneable เพื่อให้ได้สำเนาตื้นของคลาสที่ถูกต้อง

ขอบเขตที่เหมาะสมสำหรับเมธอดที่คลาสย่อยสามารถเรียกใช้ได้คืออะไร แต่คลาสอื่นไม่สามารถเรียกใช้ได้

มันprotected.

ชั้นเรียนที่ใช้Cloneableหลักสูตรจะทำให้วิธีนี้เป็นสาธารณะดังนั้นจึงสามารถเรียกจากชั้นเรียนอื่น ๆ ได้


0

วิธีการ Clone () มีการตรวจสอบ 'อินสแตนซ์ของ Cloneable หรือไม่' ภายในนี่คือวิธีที่ทีม Java อาจคิดว่าจะ จำกัด การใช้ clone () method ที่ไม่เหมาะสมวิธีการ clone () ได้รับการป้องกันเช่นเข้าถึงโดยคลาสย่อยเท่านั้น เนื่องจากออบเจ็กต์เป็นคลาสแม่ของคลาสย่อยทั้งหมดดังนั้น Clone () จึงสามารถใช้กับคลาสทั้งหมดได้หากเราไม่มีการตรวจสอบ 'อินสแตนซ์ของ Cloneable' ข้างต้น นี่คือเหตุผลที่ทีม Java อาจคิดที่จะ จำกัด การใช้ clone () ที่ไม่เหมาะสมโดยการตรวจสอบใน clone () method 'is it instance of Cloneable'

ดังนั้นคลาสใดก็ตามที่ใช้ cloneable สามารถใช้ clone () method ของ Object class ได้

นอกจากนี้เนื่องจากมีการป้องกันจึงมีให้เฉพาะคลาสย่อยที่ใช้อินเทอร์เฟซที่สามารถโคลนได้ หากเราต้องการทำให้เป็นสาธารณะวิธีนี้จะต้องถูกแทนที่โดยคลาสย่อยด้วยการนำไปใช้งานเอง


-2

ใช่ปัญหาเดียวกับที่ฉันพบ แต่ฉันแก้ปัญหาด้วยการใช้โค้ดนี้

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;
    }
}

เช่นเดียวกับที่ก่อนหน้านี้มีคนกล่าวว่า


1
CloneNotSupportedException เป็นอีกตัวอย่างหนึ่งของข้อยกเว้นที่ตรวจสอบแล้วซึ่งควรยกเลิกการเลือก (นั่นคือควรขยาย RuntimeException ไม่ใช่ Exception) แม้ว่าเมธอด clone () ในคลาส Side จะใช้ Cloneable และดังนั้นจะไม่โยน CloneNotSupportedException แต่ Side.clone () ยังคงต้องจับหรือประกาศข้อยกเว้น นี่เป็นการเพิ่มข้อยกเว้นที่ไม่จำเป็นในการจัดการเสียงรบกวนให้กับวิธีการ clone ()
Derek Mahar

-2

นอกจากนี้นักพัฒนาดวงอาทิตย์ยังเป็นเพียงมนุษย์เท่านั้นและพวกเขาทำผิดพลาดอย่างมากในการใช้วิธีการโคลนตามที่ได้รับการป้องกันซึ่งเป็นข้อผิดพลาดเดียวกันกับที่พวกเขาใช้วิธีโคลนที่ไม่ทำงานใน ArrayList! ดังนั้นโดยทั่วไปมีความเข้าใจผิดที่ลึกซึ้งยิ่งขึ้นของโปรแกรมเมอร์ Java ที่มีประสบการณ์เกี่ยวกับวิธีการโคลน

อย่างไรก็ตามเมื่อเร็ว ๆ นี้ฉันได้พบวิธีที่ง่ายและรวดเร็วในการคัดลอกวัตถุใด ๆ ที่มีเนื้อหาทั้งหมดไม่ว่าจะสร้างขึ้นอย่างไรและมีอะไรบ้างโปรดดูคำตอบของฉันที่นี่: ข้อบกพร่องในการใช้ Object.clone ()


-3

อีกครั้งกรอบ Java JDK แสดงให้เห็นถึงความคิดที่ยอดเยี่ยม:

อินเทอร์เฟซ Cloneable ไม่มี "public T clone ();" วิธีการเนื่องจากทำหน้าที่คล้ายกับแอตทริบิวต์ (เช่นต่ออนุกรมได้) ซึ่งอนุญาตให้โคลนอินสแตนซ์ได้

การออกแบบนี้ไม่มีอะไรผิดปกติเนื่องจาก:

  1. Object.clone () จะไม่ทำสิ่งที่คุณต้องการด้วยคลาสที่กำหนดเองของคุณ

  2. หากคุณมี Myclass ใช้ Cloneable => คุณเขียนทับ clone () ด้วย "public MyClass clone ()"

  3. หากคุณมี MyInterface ขยาย Cloneable และ MyClasses บางตัวใช้ MyInterface: เพียงกำหนด "โคลน MyInterface สาธารณะ ();" ในอินเทอร์เฟซและทุกวิธีที่ใช้วัตถุ MyInterface จะสามารถโคลนพวกมันได้ไม่ว่าจะเป็นคลาส MyClass ก็ตาม


2
หากคลาสของคุณได้รับการสืบทอดการใช้งานคลาสที่ได้รับมาของคุณจะไม่ปลอดภัยจนกว่าการใช้คลาสพื้นฐานของคุณจะมีวิธีการโคลนที่ปลอดภัย นอกจากนี้ยังเป็นเงื่อนไขการออกแบบที่ผิดปกติที่จะมีอินเทอร์เฟซที่ไม่มี method / attribute อินเทอร์เฟซนี้ไม่ได้บังคับให้คลาสใช้การโคลน
prap19
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.