ทำไม Cloneable ไม่เลิกใช้?


139

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

คำถามของฉันคือ: ทำไมยังไม่เลิกใช้แล้ว? หากทีม Java หลักได้ตัดสินใจว่ามันเสียแล้วพวกเขาจะต้องพิจารณาด้วย พวกเขามีเหตุผลอะไรในการทำเช่นนั้น (ใน Java 8 มันยังไม่เลิกใช้ )


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

6
@MarkoTopolnik ฉันยอมรับว่ามีบางคนในโลกที่สามารถให้คำตอบที่มีสิทธิ์ แต่ฉันไม่เชื่อว่านั่นคือการทดสอบที่เราใช้ที่นี่ เหตุผลการปิดระบุ "คำตอบสำหรับคำถามนี้มีแนวโน้มที่จะเกือบทั้งหมดขึ้นอยู่กับความคิดเห็น" ฉันสงสัยว่าจะเป็นอย่างนี้เว้นแต่เราจะโชคดีมาก
Duncan Jones

2
นี่คือ "วิธีการและเวลา" ในการคัดค้านจาก Oracle ... ( docs.oracle.com/javase/6/docs/technotes/guides/javadoc/ ...... ) ส่วนต่อประสานที่ลอกเลียนแบบอาจอยู่ในกรณี "buggy หรือไม่มีประสิทธิภาพสูง" แต่ มันเปิดกว้างสำหรับความคิดเห็น
Maxx

8
@Duncan ผมก็ยังไม่คิดว่ามันยุติธรรมที่จะผ่านการตัดสินในคำถามตามสมมติฐานของฉันเกี่ยวกับการขาดวินัยในการanswerers'ส่วนหนึ่ง หากผู้ใช้ไม่ทราบเหตุผลที่ถูกถามเขาไม่มีสิทธิ์ที่จะใช้สถานที่ตอบรับเพื่อแสดงความคิดเห็นในเรื่องดังกล่าว
Marko Topolnik

4
@lexicore ใช่ว่า --- และคุณสามารถเดิมพันที่พวกเขาได้มีการพิจารณาอย่างละเอียดว่าตัวเลือกโดยปริยายและต้องมีความแข็งแรงด้วยเหตุผลไม่ได้ที่จะเลิกมัน คำวิจารณ์ของพวกเขาเองCloneableเป็นที่รู้จักอย่างกว้างขวาง
Marko Topolnik

คำตอบ:


120

มีข้อผิดพลาดที่ส่งในปี 1997 ไปยังฐานข้อมูล Java Bugเกี่ยวกับการเพิ่มclone()วิธีการCloneableดังนั้นจะไม่ไร้ประโยชน์อีกต่อไป มันปิดด้วยความละเอียด "จะไม่แก้ไข" และเหตุผลก็เป็นดังนี้:

รีวิวซันคณะกรรมการเทคนิค (TRC) พิจารณาเรื่องนี้ที่มีความยาวและแนะนำกับการกระทำใด ๆ นอกเหนือจากการปรับปรุงเอกสารของอินเตอร์เฟซ นี่คือข้อความเต็มของคำแนะนำ:

API การโคลนวัตถุ Java ที่มีอยู่เป็นปัญหา มีวิธี "โคลน" ที่ได้รับการป้องกันบน java.lang.Object และมีอินเตอร์เฟส java.lang.Cloneable จุดประสงค์คือถ้าคลาสต้องการอนุญาตให้ผู้อื่นโคลนมันควรสนับสนุนส่วนต่อประสาน Cloneable และแทนที่วิธีการโคลนเริ่มต้นที่ได้รับการป้องกันด้วยวิธีการโคลนสาธารณะ ขออภัยด้วยเหตุผลที่หายไปอย่างรวดเร็วภายในเวลาที่กำหนดส่วนติดต่อ Cloneable ไม่ได้กำหนดวิธีการโคลน

ชุดค่าผสมนี้ทำให้เกิดความสับสนอย่างยุติธรรม บางคลาสอ้างว่าสนับสนุน Cloneable แต่ลืมเผลอสนับสนุนวิธี clone นักพัฒนากำลังสับสนเกี่ยวกับวิธีการ Cloneable ที่ควรทำงานและสิ่งที่โคลนควรทำ

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

ข้อเสนอทางเลือกคือการเพิ่มอินเตอร์เฟสใหม่ java.lang.PubliclyCloneable เพื่อสะท้อนวัตถุประสงค์ดั้งเดิมของ Cloneable โดยส่วนใหญ่ 5 ถึง 2 TRC แนะนำต่อนี้ ข้อกังวลหลักคือสิ่งนี้จะเพิ่มความสับสนมากขึ้น (รวมถึงความสับสนในการสะกดคำ!) ให้กับภาพที่สับสนอยู่แล้ว

TRC แนะนำเป็นเอกฉันท์ว่าเราควรเพิ่มเอกสารเพิ่มเติมไปยังส่วนต่อประสาน Cloneable ที่มีอยู่เพื่ออธิบายวิธีการใช้งานและอธิบาย "แนวปฏิบัติที่ดีที่สุด" สำหรับผู้ปฏิบัติงาน

ดังนั้นแม้ว่าสิ่งนี้จะไม่เกี่ยวข้องโดยตรงกับการปฏิเสธแต่เหตุผลในการไม่ทำการ Cloneable "เลิกใช้" ก็คือ Technical Review Comitee ตัดสินใจว่าการแก้ไขเอกสารที่มีอยู่จะเพียงพอเพียงพอที่จะทำให้อินเทอร์เฟซนี้มีประโยชน์ และพวกเขาก็ทำ จนกระทั่ง Java 1.4 Cloneableได้รับการบันทึกไว้ดังนี้:

คลาสใช้อินเทอร์เฟซ Cloneable เพื่อบ่งชี้ไปยังวิธีการ Object.clone () ที่ถูกกฎหมายสำหรับวิธีการนั้นเพื่อทำสำเนาฟิลด์สำหรับฟิลด์ของอินสแตนซ์ของคลาสนั้น

ความพยายามในการโคลนอินสแตนซ์ที่ไม่ได้ใช้ส่วนต่อประสาน Cloneable ทำให้เกิดข้อยกเว้น CloneNotSupportedException

อินเตอร์เฟส Cloneable ไม่ประกาศเมธอด

ตั้งแต่ Java 1.4 (ซึ่งวางจำหน่ายในเดือนกุมภาพันธ์ 2002) จนถึงรุ่นปัจจุบัน (Java 8) ดูเหมือนว่า:

คลาสใช้อินเทอร์เฟซ Cloneable เพื่อบ่งชี้ไปยังวิธีการ Object.clone () ที่ถูกกฎหมายสำหรับวิธีการนั้นเพื่อทำสำเนาฟิลด์สำหรับฟิลด์ของอินสแตนซ์ของคลาสนั้น การเรียกใช้วิธีการโคลนวัตถุในอินสแตนซ์ที่ไม่ได้ใช้ส่วนต่อประสาน Cloneable ทำให้เกิดข้อยกเว้น CloneNotSupportedException

โดยการประชุมคลาสที่ใช้อินเทอร์เฟซนี้ควรแทนที่ Object.clone (ซึ่งได้รับการป้องกัน) ด้วยวิธีสาธารณะ ดู Object.clone () สำหรับรายละเอียดเกี่ยวกับการเอาชนะวิธีนี้

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


3
และคุณรู้หรือไม่ว่าทำไมcloneวิธีการนี้จึงเป็นObjectที่แรก?
njzk2

8
@ njzk2 นั่นเป็นส่วนสำคัญของกลไก --- มันเป็นวิธีที่ใช้เวทมนตร์ระดับต่ำในระดับต่ำสุดในการคัดลอกบิตอิมเมจวัตถุสำหรับบิต
Marko Topolnik

3
@Unheilig เวทมนตร์นั้นเป็นสิ่งที่คุณไม่สามารถทำซ้ำได้ด้วยตัวสร้างสำเนา: Object#clone()สร้างอินสแตนซ์ของคลาสเดียวกันกับต้นฉบับโดยไม่ทราบคลาสนั้น ณ เวลาคอมไพล์
Marko Topolnik

4
@ AVolpe: นั่นจะไม่แก้ไขความไม่ลงรอยกันของแหล่งข้อมูลที่กล่าวถึงในคำตอบ "ที่คลาสสนับสนุนอินเทอร์เฟซ Cloneable แต่ไม่สามารถให้วิธีการโคลนสาธารณะได้" โดยเฉพาะอย่างยิ่งคลาสที่จัดให้มีวิธีการโคลนที่ไม่ใช่แบบสาธารณะจะใช้งานไม่ได้
Louis Wasserman

2
ประวัติศาสตร์ที่ดี นี่คือลิงก์โดยตรงไปยังฐานข้อมูลบั๊ก ฉันได้เพิ่มประวัติศาสตร์บางมากขึ้นที่นั่นและผมเคยยกมาบางส่วนของมันในคำตอบของฉัน
Stuart Marks

64

คำตอบสั้น ๆ เกี่ยวกับ "ทำไมจึงไม่Cloneableคัดค้าน" (หรือจริง ๆ แล้วทำไมถึงไม่Xคัดค้านสำหรับใครX) ก็คือไม่ได้ให้ความสนใจมากนักในการคัดค้านพวกเขา

สิ่งส่วนใหญ่ที่ไม่ได้รับการสนับสนุนเมื่อเร็ว ๆ นี้ถูกคัดค้านเนื่องจากมีแผนเฉพาะที่จะลบออก ตัวอย่างเช่น, addPropertyChangeListenerและremovePropertyChangeListenerวิธีการของLogManagerถูกเลิกใช้ใน Java SE 8โดยมีความตั้งใจที่จะลบมันใน Java SE 9 (เหตุผลก็คือพวกเขามีความซับซ้อนโดยไม่จำเป็นซึ่งกันและกันโมดูล) API เหล่านี้ได้ถูกลบออกจากการพัฒนาJDK 9 ก่อนสร้าง (โปรดทราบว่าการเรียกฟังการเปลี่ยนแปลงคุณสมบัติที่คล้ายกันก็ถูกลบออกจากPack200ด้วย; ดูJDK-8029806 )

ไม่มีแผนดังกล่าวที่คล้ายกันที่มีอยู่เพื่อสำหรับและCloneableObject.clone()

คำตอบที่ยาวขึ้นจะเกี่ยวข้องกับการพูดคุยคำถามเพิ่มเติมเช่นสิ่งที่คาดว่าจะเกิดขึ้นกับ API เหล่านี้ต้นทุนหรือผลประโยชน์ใดที่จะเกิดขึ้นกับแพลตฟอร์มหากเลิกใช้แล้วและสิ่งใดบ้างที่สื่อสารกับนักพัฒนาเมื่อ API เลิกใช้ ผมสำรวจหัวข้อนี้ในการพูดคุย JavaOne ล่าสุดของฉัน, ตราสารหนี้และการเลิกใช้ (สไลด์มีให้ที่ลิงก์นั้นวิดีโอที่นี่ ) ปรากฎว่า JDK นั้นไม่ได้สอดคล้องกันอย่างมากในการใช้งานการคัดค้าน มันถูกใช้เพื่อหมายถึงสิ่งต่าง ๆ รวมถึงตัวอย่าง

  • นี้เป็นอันตรายและคุณควรจะตระหนักถึงความเสี่ยงของการใช้มัน (ตัวอย่าง: Thread.stop(), Thread.resume()และThread.suspend())

  • สิ่งนี้กำลังจะถูกลบในการเปิดตัวในอนาคต

  • สิ่งนี้ล้าสมัยและเป็นความคิดที่ดีที่คุณจะใช้สิ่งที่แตกต่าง (ตัวอย่าง: วิธีการหลายวิธีในjava.util.Date)

ทั้งหมดเหล่านี้มีความหมายที่แตกต่างกันและส่วนย่อยต่าง ๆ ของพวกเขานำไปใช้กับสิ่งต่าง ๆ ที่เลิกใช้แล้ว และบางส่วนของพวกเขานำไปใช้กับสิ่งที่ไม่ได้เลิกใช้ (แต่อาจจะเลิกใช้แล้ว)

CloneableและObject.clone()"แตก" ในแง่ที่ว่าพวกเขามีข้อบกพร่องในการออกแบบและใช้งานได้ยาก อย่างไรก็ตามclone()ยังคงเป็นวิธีที่ดีที่สุดในการคัดลอกอาร์เรย์และการโคลนมีประโยชน์ จำกัด ในการทำสำเนาอินสแตนซ์ของคลาสที่มีการใช้งานอย่างระมัดระวัง การลบการโคลนจะเป็นการเปลี่ยนแปลงที่เข้ากันไม่ได้ซึ่งจะทำลายหลายสิ่ง การดำเนินการโคลนอาจจะ reimplemented วิธีที่แตกต่างกัน Object.clone()แต่ก็อาจจะช้ากว่า

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

ขณะที่สิ่งต่าง ๆ ยืนอยู่ "การคัดค้าน" ดูเหมือนจะบ่งบอกถึงการเอาออกในที่สุด - แม้ว่าจะมีการลบคุณลักษณะที่คัดค้านจำนวนเล็กน้อยที่หายไปอย่างไม่เคยมีมาก่อน บางทีในอนาคตอาจมีการใช้การทำเครื่องหมายทางเลือกซึ่งนำผู้พัฒนาไปใช้กลไกทางเลือกแทน

UPDATE

ฉันได้เพิ่มประวัติเพิ่มเติมบางอย่างเพื่อรายงานข้อผิดพลาด แฟรงก์สะใจเป็น implementor JVM และผู้เขียนร่วมของสเป JVM ต้นได้แสดงความคิดเห็นบางอย่างในการตอบสนองต่อความคิดเห็น "หายไปในหมอกของเวลา" ในคำแนะนำของ TRC ยกมาในคำตอบอื่นฉันอ้างส่วนที่เกี่ยวข้องที่นี่; ข้อความเต็มอยู่ในรายงานข้อผิดพลาด

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

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

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


3
หนึ่งคำตอบที่ดีคุณมีที่นี่ครับ โดยเฉพาะอย่างยิ่งฉันชอบที่คุณไม่เพียง แต่โยนObject.clone()เข้าไปในกองไฟเพียงเพราะคนอื่นต้องการ แต่คุณยินดีที่จะให้เหตุผลและนำสิ่งที่ดีของมันขึ้นมา
icza

2
อย่างไรก็ตามการโคลน () ยังคงเป็นวิธีที่ดีที่สุดในการคัดลอกอาร์เรย์และการโคลนมีประโยชน์ที่ จำกัด ในการทำสำเนาอินสแตนซ์ของคลาสที่มีการใช้งานอย่างระมัดระวัง ฉันอยู่ภายใต้การแสดงผลด้วยการแก้ไข 6428,387 เส้นทางรหัสทั้งหมด (โคลนใหม่ / arrayCopy, Arrays.copyOf) ทำให้เกิดการอินทรินเดียวกัน มีอะไรเปลี่ยนแปลงบ้างไหม?
bestsss

2
@ bestss ฉันไม่คิดว่าarray.clone()จะต้องเร็วกว่าทางเลือกใด ๆ จากมุมมอง API เป็นวิธีที่กระชับที่สุดในการทำซ้ำอาร์เรย์ Arrays.copyOf(array, newlen)เข้ามาใกล้ แต่ต้องใช้พารามิเตอร์ความยาวซึ่งซ้ำซ้อนหากคุณไม่ได้เปลี่ยนความยาว
Stuart Marks

2
@ Holger ใช่เท่าที่เราจะเห็นได้ว่านี่เป็นการลบครั้งแรกของ API ตั้งแต่ 1.1 โปรดทราบด้วยว่าแม้ว่าเราจะเห็นด้วยว่าThread.suspend()และThread.stop()(ไม่มีข้อโต้แย้ง) เป็นอันตรายพวกเขาอาจจะไม่ถูกลบออกหรือเปลี่ยนเป็นข้อยกเว้นโดยไม่มีเงื่อนไข - เพราะผู้คนใช้งานพวกเขาจริงๆ! สันนิษฐานว่าพวกเขายินดีที่จะรับความเสี่ยง หนึ่งในปัจจัยบรรเทาที่มีต่อผู้ฟังการเปลี่ยนแปลงคุณสมบัติคือพวกเขาถูกนำมาใช้น้อยมากดังนั้นผลกระทบของการลบจะมีขนาดเล็ก
Stuart Marks

2
@ Holger Conceptually java.beansสามารถทำให้เป็นอิสระจากjava.desktopถั่วเป็นเพียงไลบรารี API สำหรับคุณสมบัติ น่าเสียดายถ้าคุณขุดเข้าไปใน beans API มีการขึ้นต่อกันมากมายบน AWT การใช้งานมีมากยิ่งขึ้น แน่นอนว่าอาจเป็นไปได้ที่จะคลี่คลายพวกเขา แต่การทำเช่นนั้นดูเหมือนว่าจะทำงานได้มากกว่าพูดปิดการบันทึกจากถั่ว ความพยายามในการทำให้เป็นโมดูลทั้งหมดนั้นเกี่ยวกับการทำ disentangling นี้ ไม่ต้องสงสัยอีกต่อไปสามารถทำได้ แต่จิ๊กซอว์จะใช้เวลานานขึ้น
Stuart Marks

-1

ทำไมถึงยังไม่เลิกใช้

เนื่องจาก JCP ไม่เห็นสมควรที่จะทำเช่นนั้นและอาจไม่เคยทำเช่นนั้น ถามพวกเขา. คุณถามผิดที่

อะไรคือสาเหตุที่ทำให้สิ่งนี้อยู่ใน Java API

ไม่มีใครจะลบอะไรจาก Java API เนื่องจากข้อกำหนดด้านความเข้ากันได้ย้อนหลัง ครั้งสุดท้ายที่เกิดขึ้นคือการเปลี่ยนแปลงรูปแบบกิจกรรม AWT ระหว่าง 1.0 และ 1.1 ในปี 1996/7


17
พวกเขาลบได้อย่างมีประสิทธิภาพThread.stop(Throwable)โดยการเปลี่ยนสัญญาที่จะโยนUnsupportedOperationExceptionไปยังผู้โทร (ไม่ได้ไปที่หัวข้อเป้าหมาย!)
Marko Topolnik

22
เกิดอะไรขึ้นในเวลาเดียวกัน? การลบการThread.stop(Throwable)ทำงานของเกิดขึ้นใน Java 8 อย่างไรก็ตามคำแนะนำอย่างไม่มีเงื่อนไขในการ "ถามพวกเขา" ผิดเพราะวันนี้หัวหน้าสถาปนิก Java ตัวเองเป็นสมาชิกที่ใช้งานใน Stack Overflow เขาไม่รำคาญที่จะตอบอะไรนอกจากคำถามที่เกี่ยวข้องกับสตรีม
Marko Topolnik

13
นอกจากนี้คำถามของ OP ไม่ได้เกี่ยวกับการลบออกแต่เกี่ยวกับการคัดค้านและการเลิกใช้อย่างชัดเจนเกิดขึ้นตลอดเวลา
Marko Topolnik

17
@EJP ฉันไม่ได้ถามว่าCloneableจะถูกลบออกจาก Java API หรือไม่ ฉันถามว่าทำไมมันจะไม่ลดลง และฉันคิดว่านี่เป็นสถานที่ที่เหมาะสำหรับการถาม
เก่า

5
@VaheHarutyunyan ขอบคุณสำหรับการตะโกนออกมา แต่ฉันไม่ใช่สถาปนิก Java ฉันเป็นวิศวกรในกลุ่ม JDK ของ Oracle ซึ่งดูแลสิ่งนี้
Stuart Marks
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.