Tại sao Clonizable không được phản đối?


139

Người ta thường hiểu rằng Cloneablegiao diện trong Java bị hỏng. Có nhiều lý do cho việc này, mà tôi sẽ không đề cập; những người khác đã làm điều đó. Đó cũng là vị trí của chính các kiến ​​trúc sư Java .

Do đó, câu hỏi của tôi là: tại sao vẫn chưa được phản đối? Nếu nhóm Java cốt lõi đã quyết định rằng nó bị hỏng, thì họ cũng phải xem xét khấu hao. Lý do của họ chống lại việc đó là gì (trong Java 8 vẫn không được phản đối )?


41
Câu hỏi này không "chủ yếu dựa trên quan điểm", vì nhiều người dường như cảm thấy có quyền phán xét. Những người không có gì hơn là một ý kiến ​​về lý do đơn giản là không đủ điều kiện để trả lời. Tuy nhiên, sự thật là bạn chỉ có cơ hội từ xa để nhận được câu trả lời có thẩm quyền ở đây. Cũng đúng là câu hỏi của bạn không phải là về một vấn đề có thể giải quyết được, do đó, ít nhất đó là vấn đề ngoài lề.
Marko Topolnik

6
@MarkoTopolnik Tôi đồng ý rằng có một số người trên thế giới có thể cung cấp câu trả lời có thẩm quyền, nhưng tôi không tin rằng đó là bài kiểm tra mà chúng tôi áp dụng ở đây. Lý do đóng cửa nêu rõ "câu trả lời cho câu hỏi này sẽ có xu hướng gần như hoàn toàn dựa trên ý kiến". Tôi nghi ngờ đó sẽ là trường hợp ở đây, trừ khi chúng ta rất may mắn.
Duncan Jones

2
Đây là "Cách thức và thời gian" không dùng nữa từ Oracle ... ( docs.oracle.com/javase/6/docs/technotes/guides/javadoc/ .) Giao diện có thể sao chép có thể rơi vào trường hợp "lỗi, hoặc không hiệu quả cao" nhưng nó rất cởi mở với các ý kiến.
Maxx

8
@Duncan Tôi vẫn không cho rằng công bằng khi đưa ra phán xét cho câu hỏi dựa trên giả định của tôi về việc thiếu kỷ luật đối với người trả lời . Nếu người dùng không biết lý do được hỏi về, họ không có quyền lạm dụng cơ sở trả lời để trình bày ý kiến ​​của mình về vấn đề này.
Marko Topolnik

4
@lexicore Vâng, chính xác --- và bạn có thể đặt cược rằng họ đã xem xét kỹ lưỡng tùy chọn đó và theo ngụ ý phải có lý do mạnh mẽ để không từ chối nó. Những lời chỉ trích của riêng họ Cloneableđược biết đến rộng rãi.
Marko Topolnik

Câu trả lời:


120

Có một lỗi được gửi vào năm 1997 cho Cơ sở dữ liệu lỗi Java về việc thêm clone()phương thức vào Cloneable, vì vậy nó sẽ không còn vô dụng nữa. Nó đã được đóng lại với độ phân giải "sẽ không sửa chữa" và biện minh như sau:

Ủy ban đánh giá kỹ thuật của Sun (TRC) đã xem xét vấn đề này từ lâu và khuyến nghị không nên thực hiện bất kỳ hành động nào ngoài việc cải thiện tài liệu của giao diện Clonizable hiện tại . Dưới đây là toàn văn khuyến nghị:

Các API nhân bản đối tượng Java hiện có là vấn đề. Có một phương thức "nhân bản" được bảo vệ trên java.lang.Object và có một giao diện java.lang.Clonizable. Ý định là nếu một lớp muốn cho phép người khác sao chép nó, thì nó nên hỗ trợ giao diện Clonizable và ghi đè phương thức nhân bản được bảo vệ mặc định bằng phương thức nhân bản công khai. Thật không may, vì lý do thuận tiện bị mất trong thời gian, giao diện Clonizable không xác định phương pháp nhân bản.

Sự kết hợp này dẫn đến một sự nhầm lẫn khá lớn. Một số lớp yêu cầu hỗ trợ Clonizable, nhưng vô tình quên hỗ trợ phương thức clone. Các nhà phát triển bối rối về cách Clonizable được cho là hoạt động và những gì được cho là nhân bản.

Thật không may, việc thêm một phương thức "nhân bản" vào Clonizable sẽ là một thay đổi không tương thích. Nó sẽ không phá vỡ tính tương thích nhị phân, nhưng nó sẽ phá vỡ tính tương thích nguồn. Bằng chứng giai thoại cho thấy rằng trong thực tế có một số trường hợp các lớp hỗ trợ giao diện Clonizable nhưng không cung cấp phương thức nhân bản công khai. Sau khi thảo luận, TRC nhất trí khuyến nghị rằng chúng ta KHÔNG nên sửa đổi giao diện Clonizable hiện có, vì tác động tương thích.

Một đề xuất thay thế là thêm giao diện mới java.lang.PubliclyClonizable để phản ánh mục đích dự định ban đầu của Clonizable. Theo đa số 5 đến 2, TRC khuyến nghị chống lại điều này. Mối quan tâm chính là điều này sẽ thêm sự nhầm lẫn (bao gồm cả nhầm lẫn chính tả!) Cho một hình ảnh đã bị nhầm lẫn.

TRC nhất trí khuyến nghị rằng chúng ta nên thêm tài liệu bổ sung vào giao diện Clonizable hiện có để mô tả rõ hơn cách sử dụng nó và để mô tả "các thực tiễn tốt nhất" cho người triển khai.

Vì vậy, mặc dù điều này không trực tiếp về việc phản đối , nhưng lý do không khiến Clonizable "không dùng nữa" là vì Tạp chí Kỹ thuật Comitee đã quyết định rằng sửa đổi tài liệu hiện có sẽ đủ để làm cho giao diện này hữu ích. Và họ đã làm như vậy. Cho đến Java 1.4, Cloneableđã được ghi lại như sau:

Một lớp thực hiện giao diện Clonizable để chỉ ra phương thức Object.clone () rằng phương thức đó là hợp pháp để tạo một bản sao trường cho các trường hợp của lớp đó.

Nỗ lực sao chép các trường hợp không triển khai giao diện Clonizable dẫn đến ngoại lệ CloneNotSupportedException bị ném.

Giao diện Clonizable tuyên bố không có phương pháp.

Kể từ Java 1.4 (được phát hành vào tháng 2 năm 2002) cho đến phiên bản hiện tại (Java 8), nó trông như thế này:

Một lớp thực hiện giao diện Clonizable để chỉ ra phương thức Object.clone () rằng phương thức đó là hợp pháp để tạo một bản sao trường cho các trường hợp của lớp đó. Gọi phương thức nhân bản của Object trên một cá thể không triển khai kết quả giao diện Clonizable trong trường hợp ngoại lệ CloneNotSupportedException bị ném.

Theo quy ước, các lớp thực hiện giao diện này sẽ ghi đè lên Object.clone (được bảo vệ) bằng một phương thức công khai. Xem Object.clone () để biết chi tiết về ghi đè phương thức này.

Lưu ý rằng giao diện này không chứa phương thức sao chép. Do đó, không thể sao chép một đối tượng chỉ nhờ vào thực tế là nó thực hiện giao diện này. Ngay cả khi phương pháp nhân bản được gọi một cách phản xạ, không có gì đảm bảo rằng nó sẽ thành công.


3
và bạn có biết tại sao clonephương pháp này ở Objectvị trí đầu tiên không?
njzk2

8
@ njzk2 Đó là một phần thiết yếu của cơ chế --- đó là phương pháp thực hiện phép thuật ngoại cảm ở mức độ thấp để sao chép bit hình ảnh đối tượng thành bit.
Marko Topolnik

3
@Unheilig Phép thuật đó là thứ bạn không thể sao chép với một hàm tạo sao chép: Object#clone()tạo ra một thể hiện của cùng một lớp với bản gốc mà không có lớp đó được biết đến vào thời gian biên dịch.
Marko Topolnik

4
@AVolpe: Điều đó sẽ không khắc phục sự không tương thích nguồn được đề cập trong câu trả lời "nơi các lớp hỗ trợ giao diện Clonizable nhưng không cung cấp phương thức nhân bản công khai." Cụ thể, các lớp cung cấp một phương thức nhân bản không công khai sẽ bị phá vỡ.
Louis Wasserman

2
Lịch sử tốt đẹp. Đây là một liên kết trực tiếp đến cơ sở dữ liệu lỗi. Tôi đã thêm một số lịch sử ở đó và tôi đã trích dẫn một phần của nó trong câu trả lời của tôi .
Stuart Marks

64

Câu trả lời ngắn gọn cho "tại sao không Cloneableđược tán thành?" (hoặc thực sự, tại sao không Xđược phản đối, vì bất kỳ X) là đã không có nhiều sự chú ý để phản đối họ.

Hầu hết những thứ bị phản đối gần đây đều bị phản đối vì có một kế hoạch cụ thể để loại bỏ chúng. Ví dụ, addPropertyChangeListenerremovePropertyChangeListenercác phương thức của LogManager không được dùng trong Java SE 8 với ý định loại bỏ chúng trong Java SE 9. (Lý do là chúng phụ thuộc lẫn nhau vào mô-đun phức tạp không cần thiết.) Thật vậy, các API này đã bị xóa khỏi quá trình phát triển JDK 9 ban đầu. xây dựng. (Lưu ý rằng các lệnh gọi người nghe thay đổi thuộc tính tương tự cũng bị xóa khỏi Pack200; xem JDK-8029806 .)

Không có kế hoạch tương tự như vậy tồn tại cho CloneableObject.clone().

Câu trả lời dài hơn sẽ liên quan đến việc thảo luận các câu hỏi tiếp theo, chẳng hạn như những gì người ta có thể mong đợi xảy ra với các API này, chi phí hoặc lợi ích nào sẽ tích lũy cho nền tảng nếu chúng không được chấp nhận và những gì được truyền đạt tới các nhà phát triển khi API bị từ chối. Tôi đã khám phá chủ đề này trong cuộc nói chuyện, Nợ và Khấu hao JavaOne gần đây của tôi . (Các slide có sẵn tại liên kết đó; video ở đây .) Hóa ra chính JDK đã không nhất quán trong việc sử dụng khấu hao. Nó được sử dụng để chỉ một số điều khác nhau, bao gồm cả, ví dụ,

  • Đây là nguy hiểm và bạn cần phải nhận thức những nguy cơ của việc sử dụng nó (ví dụ: Thread.stop(), Thread.resume(), và Thread.suspend()).

  • Điều này sẽ được gỡ bỏ trong một bản phát hành trong tương lai

  • Điều này đã lỗi thời và bạn nên sử dụng một cái gì đó khác biệt (ví dụ: nhiều phương pháp trong java.util.Date )

Tất cả đều là những ý nghĩa riêng biệt và các tập hợp con khác nhau của chúng áp dụng cho những thứ khác nhau không được dùng nữa. Và một số tập hợp con trong số chúng áp dụng cho những thứ không được dùng nữa (nhưng có lẽ không nên dùng nữa).

CloneableObject.clone()bị "phá vỡ" theo nghĩa là chúng có lỗi thiết kế và khó sử dụng chính xác. Tuy nhiên,clone() vẫn là cách tốt nhất để sao chép mảng và nhân bản có một số hữu ích hạn chế để tạo các bản sao của các thể hiện của các lớp được thực hiện cẩn thận. Loại bỏ nhân bản sẽ là một thay đổi không tương thích sẽ phá vỡ rất nhiều thứ. Một hoạt động nhân bản có thể được thực hiện lại theo một cách khác, nhưng nó có thể sẽ chậm hơn Object.clone().

Tuy nhiên, đối với hầu hết mọi thứ, một nhà xây dựng sao chép thích hợp hơn để nhân bản. Vì vậy, có lẽ đánh dấuCloneable là "lỗi thời" hoặc "thay thế" hoặc một cái gì đó tương tự sẽ là phù hợp. Điều này sẽ nói với các nhà phát triển rằng họ có thể muốn tìm ở nơi khác, nhưng nó sẽ không báo hiệu rằng cơ chế nhân bản có thể bị xóa trong một bản phát hành trong tương lai. Thật không may, không có điểm đánh dấu như vậy tồn tại.

Khi mọi thứ đứng vững, "sự phản đối" dường như ngụ ý loại bỏ cuối cùng - mặc dù thực tế là một số lượng nhỏ các tính năng bị phản đối đã bị loại bỏ - và vì vậy sự phản đối dường như không được bảo đảm cho cơ chế nhân bản. Có lẽ trong tương lai, một dấu hiệu thay thế có thể được áp dụng để hướng các nhà phát triển sử dụng các cơ chế thay thế.

CẬP NHẬT

Tôi đã thêm một số lịch sử bổ sung vào báo cáo lỗi . Frank Yellin, một người triển khai JVM sớm và là đồng tác giả của đặc tả JVM, đã đưa ra một số nhận xét để phản hồi lại nhận xét "bị mất trong thời gian" trong khuyến nghị TRC được trích dẫn trong câu trả lời khác . Tôi đã trích dẫn các phần có liên quan ở đây; thông báo đầy đủ là trong báo cáo lỗi.

Clonizable không có phương thức nào cho cùng một lý do mà serializable không có. Clonizable chỉ ra một thuộc tính của lớp, thay vì nói cụ thể bất cứ điều gì về các phương thức mà lớp hỗ trợ.

Trước khi phản ánh, chúng tôi cần một phương thức riêng để tạo một bản sao nông của Object. Do đó Object.clone () đã ra đời. Rõ ràng là nhiều lớp sẽ muốn ghi đè phương thức này và không phải lớp nào cũng muốn được sao chép. Do đó Clonizable được sinh ra để chỉ ra ý định của lập trình viên.

Vì vậy, trong ngắn hạn. Mục đích của Clonizable không phải là để chỉ ra rằng bạn có phương thức clone () công khai. Đó là để chỉ ra rằng bạn sẵn sàng được nhân bản bằng Object.clone () và tùy thuộc vào việc triển khai để quyết định có công khai clone () hay không.


3
Một câu trả lời tốt bạn có ở đây thưa ông. Tôi đặc biệt thích rằng bạn không chỉ ném Object.clone()vào lửa chỉ vì những người khác muốn, nhưng bạn sẵn sàng lý luận và đưa ra những điều tốt đẹp của nó.
icza

2
Tuy nhiên, clone () vẫn là cách tốt nhất để sao chép mảng và nhân bản có một số hữu ích hạn chế để tạo các bản sao của các thể hiện của các lớp được thực hiện cẩn thận. Tôi đã có ấn tượng với bản sửa lỗi của 6428387, tất cả các đường dẫn mã (clone, new / ArrayCopy, Arrays.copyOf) đều dẫn đến cùng một nội tại. Gần đây có gì thay đổi không?
bestsss

2
@bestsss Tôi không nghĩ array.clone()là nhất thiết phải nhanh hơn bất kỳ giải pháp thay thế nào. Từ phối cảnh API, đây là cách ngắn gọn nhất để sao chép một mảng. Arrays.copyOf(array, newlen)đến gần, nhưng nó yêu cầu một tham số độ dài, dự phòng nếu bạn không thay đổi độ dài.
Stuart Marks

2
@Holger Có theo như chúng tôi có thể thấy đây là lần xóa API thực tế đầu tiên kể từ 1.1. Cũng lưu ý rằng mặc dù chúng tôi đồng ý rằng Thread.suspend()Thread.stop()(không tranh luận) là nguy hiểm, nhưng có lẽ chúng sẽ không bị xóa - hoặc thay đổi để ném một ngoại lệ vô điều kiện - bởi vì mọi người thực sự sử dụng chúng! Có lẽ họ sẵn sàng chịu rủi ro. Một trong những yếu tố giảm thiểu với người nghe thay đổi tài sản là chúng rất hiếm khi được sử dụng, vì vậy tác động của việc loại bỏ chúng là rất nhỏ.
Stuart Marks

2
@Holger Về mặt khái niệm java.beanscó thể được tạo ra độc lập java.desktopvì các bean chỉ là một API thư viện cho các thuộc tính. Thật không may nếu bạn đào sâu vào API đậu, có rất nhiều phụ thuộc vào AWT. Việc thực hiện thậm chí còn nhiều hơn. Chắc chắn có thể giải thoát chúng, nhưng làm điều đó có vẻ như nhiều công việc hơn, nói, không đồng ý đăng nhập từ đậu. Toàn bộ nỗ lực mô đun hóa là về việc giải quyết vấn đề này; chắc chắn có thể làm được nhiều hơn thế, nhưng sau đó Jigsaw sẽ còn lâu hơn nữa.
Stuart Marks

-1

Tại sao nó không được phản đối?

Bởi vì JCP chưa thấy phù hợp để làm như vậy và có thể không bao giờ làm như vậy. Hỏi họ. Bạn đang hỏi sai chỗ.

Các lý do đằng sau việc giữ điều này trong API Java là gì

Không ai sẽ xóa bất cứ thứ gì khỏi API Java, vì yêu cầu tương thích ngược. Lần cuối cùng xảy ra là sự thay đổi trong mô hình sự kiện AWT giữa 1.0 và 1.1 vào năm 1996/7.


17
Họ đã (loại bỏ) một cách hiệu quả Thread.stop(Throwable)bằng cách thay đổi hợp đồng của mình để luôn ném UnsupportedOperationExceptioncho người gọi (không phải cho chủ đề đích!).
Marko Topolnik

22
Điều gì đã diễn ra cùng một lúc? Việc loại bỏ Thread.stop(Throwable)chức năng của đã xảy ra trong Java 8. Dù sao, lời khuyên không đủ tiêu chuẩn để "hỏi họ" là sai bởi vì ngày nay, chính kiến ​​trúc sư Java là một thành viên tích cực trên Stack Overflow. Anh ta không thèm trả lời bất cứ điều gì ngoài những câu hỏi liên quan đến Streams.
Marko Topolnik

13
Hơn nữa, câu hỏi của OP không phải là về việc loại bỏ , mà là về sự phản đối , và sự phản đối rõ ràng đã xảy ra từ lâu.
Marko Topolnik

17
@EJP Tôi không hỏi liệu Cloneablecó bị xóa khỏi API Java không. Tôi đang hỏi tại sao nó sẽ không bị mất. Và tôi nghĩ rằng đây là một nơi hoàn hảo để hỏi.
Kao

5
@VaheHarutyunyan Cảm ơn bạn đã hét to, nhưng tôi không phải là kiến ​​trúc sư Java. Tôi là một kỹ sư trong nhóm JDK của Oracle, công ty duy trì công cụ này.
Stuart Marks
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.