Câu trả lời:
Thực tế là bản sao được bảo vệ là vô cùng đáng ngờ - cũng như thực tế là clone
phương thức không được khai báo trong Cloneable
giao diện.
Nó làm cho phương pháp này trở nên vô dụng trong việc lấy bản sao dữ liệu vì bạn không thể nói :
if(a instanceof Cloneable) {
copy = ((Cloneable) a).clone();
}
Tôi nghĩ rằng thiết kế của Cloneable
bây giờ phần lớn được coi là một sai lầm (trích dẫn bên dưới). Tôi thường muốn có thể triển khai một giao diện Cloneable
nhưng không nhất thiết phải tạo giao diệnCloneable
(tương tự như việc sử dụng Serializable
). Điều này không thể được thực hiện nếu không có phản ánh:
ISomething i = ...
if (i instanceof Cloneable) {
//DAMN! I Need to know about ISomethingImpl! Unless...
copy = (ISomething) i.getClass().getMethod("clone").invoke(i);
}
Trích dẫn từ Java hiệu quả của Josh Bloch :
"Giao diện có thể sao chép được nhằm mục đích làm giao diện mixin cho các đối tượng quảng cáo rằng chúng cho phép nhân bản. Thật không may, nó không phục vụ mục đích này ... Đây là cách sử dụng giao diện rất không điển hình và không phải là giao diện được mô phỏng ... Để việc triển khai giao diện có bất kỳ ảnh hưởng nào đến một lớp, nó và tất cả các lớp cha của nó phải tuân theo giao thức khá phức tạp, không thể thực thi và phần lớn là không có tài liệu "
Serializable
- tùy thuộc vào việc triển khai để quyết định có thực hiện hay không Serializable
. Tôi đã mở rộng điều này đến Cloneable
- nó không phải là thứ mà một giao diện nên mở rộng - nhưng việc triển khai một giao diện là miễn phí Cloneable
. Vấn đề là, nếu bạn có một tham số của kiểu giao diện, bạn hỏi nó liệu nó có thể sao chép được không; nhưng sau đó bạn thực sự không thể sao chép nó!
Giao diện Clonable chỉ là một điểm đánh dấu cho biết lớp có thể hỗ trợ sao chép. Phương thức được bảo vệ bởi vì bạn không nên gọi nó trên đối tượng, bạn có thể (và nên) ghi đè nó thành public.
Từ CN:
Trong đối tượng lớp, phương thức clone () được khai báo là bảo vệ. Nếu tất cả những gì bạn làm là triển khai Cloneable, chỉ các lớp con và các thành viên của cùng một gói mới có thể gọi clone () trên đối tượng. Để cho phép bất kỳ lớp nào trong bất kỳ gói nào truy cập phương thức clone (), bạn sẽ phải ghi đè nó và khai báo nó công khai, như được thực hiện bên dưới. (Khi bạn ghi đè một phương thức, bạn có thể đặt nó ít riêng tư hơn, nhưng không riêng tư hơn. Ở đây, phương thức protected clone () trong Đối tượng đang được ghi đè như một phương thức công khai.)
Set
clone
được bảo vệ vì nó là thứ cần được ghi đè để nó dành riêng cho lớp hiện tại. Mặc dù có thể tạo ra một clone
phương thức công khai có thể sao chép bất kỳ đối tượng nào nhưng điều này sẽ không tốt bằng một phương thức được viết riêng cho lớp cần nó.
Phương thức Clone không thể được sử dụng trực tiếp trên bất kỳ đối tượng nào, đó là lý do tại sao nó được dùng để ghi đè bởi lớp con.
Tất nhiên nó có thể được công khai và chỉ cần đưa ra một ngoại lệ thích hợp khi không thể nhân bản, nhưng tôi nghĩ rằng điều đó sẽ gây hiểu lầm.
Cách sao chép được thực hiện ngay bây giờ khiến bạn suy nghĩ về lý do tại sao bạn muốn sử dụng nhân bản và cách bạn muốn đối tượng của mình được nhân bản.
Nó được bảo vệ bởi vì việc triển khai mặc định thực hiện một bản sao thành viên nông của tất cả các trường (bao gồm cả private), phá vỡ hàm tạo . Đây không phải là thứ mà một đối tượng có thể được thiết kế để xử lý ngay từ đầu (ví dụ: nó có thể theo dõi các cá thể đối tượng đã tạo trong danh sách được chia sẻ hoặc thứ gì đó tương tự).
Vì lý do tương tự, việc triển khai mặc định của clone()
sẽ ném nếu đối tượng mà nó được gọi không triển khai Cloneable
. Đó là một hoạt động tiềm ẩn không an toàn với những hậu quả sâu rộng và do đó tác giả của lớp phải chọn tham gia một cách rõ ràng.
Từ javadoc của 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.
Vì vậy, bạn có thể gọi sao chép trên mọi đối tượng nhưng điều này sẽ cho bạn hầu hết thời gian không phải là kết quả bạn muốn hoặc một ngoại lệ. Nhưng chỉ được khuyến khích nếu bạn triển khai có thể nhân bản.
IMHO nó đơn giản như sau:
#clone
không được gọi trên các đối tượng không thể sao chép, do đó nó không được công khai#clone
phải được gọi bởi các lớp con ob Object
triển khai Cloneable để có được bản sao nông của lớp phù hợpPhạm vi phù hợp cho các phương thức có thể được gọi bởi các lớp con, nhưng không phải bởi các lớp khác?
Nó protected
.
Các lớp thực hiện Cloneable
tất nhiên sẽ đặt phương thức này ở chế độ công khai để nó có thể được gọi từ các lớp khác.
Phương thức Clone () có kiểm tra nội bộ 'phiên bản có thể sao chép hay không'. Đây là cách nhóm Java có thể nghĩ rằng sẽ hạn chế việc sử dụng không đúng phương thức clone () method.clone () được bảo vệ tức là chỉ được truy cập bởi các lớp con. Vì đối tượng là lớp cha của tất cả các lớp con, vì vậy phương thức Clone () có thể được sử dụng bởi tất cả các lớp nếu chúng ta không kiểm tra ở trên về 'instance of Cloneable'. Đây là lý do mà nhóm Java có thể đã nghĩ đến việc hạn chế việc sử dụng không đúng cách clone () bằng cách kiểm tra phương thức clone () 'nó có phải là bản sao của Cloneable không'.
Do đó bất kỳ lớp nào triển khai cloneable đều có thể sử dụng phương thức clone () của lớp Object.
Cũng vì nó được bảo vệ nên nó chỉ có sẵn cho những lớp con triển khai giao diện có thể nhân bản. Nếu chúng ta muốn đặt nó ở chế độ công khai, phương thức này phải được ghi đè bởi lớp con với việc triển khai riêng của chúng.
Vâng, cùng một vấn đề mà tôi đã gặp. Nhưng tôi giải quyết nó bằng cách triển khai mã này
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;
}
}
Cũng như trước đó ai đó đã nói.
Chà, các nhà phát triển mặt trời cũng chỉ là con người, và họ thực sự đã mắc sai lầm lớn khi triển khai phương thức nhân bản được bảo vệ, sai lầm giống như khi họ triển khai phương thức nhân bản không hoạt động trong ArrayList! Vì vậy, nói chung, tồn tại sự hiểu lầm sâu sắc hơn của các lập trình viên Java có kinh nghiệm về phương thức nhân bản.
Tuy nhiên, gần đây tôi đã tìm thấy một giải pháp nhanh chóng và dễ dàng để sao chép bất kỳ đối tượng nào với tất cả nội dung của nó, bất kể nó được xây dựng như thế nào và chứa những gì, hãy xem câu trả lời của tôi tại đây: Lỗi khi sử dụng Object.clone ()
Một lần nữa, khung Java JDK cho thấy tư duy xuất sắc:
Giao diện nhân bản không chứa "public T clone ();" vì nó hoạt động giống một thuộc tính hơn (ví dụ: Serializable) cho phép một thể hiện nó được nhân bản.
Không có gì sai với thiết kế này vì:
Object.clone () sẽ không làm những gì bạn muốn với lớp được xác định tùy chỉnh của bạn.
Nếu bạn có Myclass triển khai Cloneable => bạn ghi đè clone () bằng "public MyClass clone ()"
Nếu bạn có MyInterface mở rộng Cloneable và một số MyClass triển khai MyInterface: chỉ cần xác định "public MyInterface clone ();" trong giao diện và mọi phương thức sử dụng các đối tượng MyInterface sẽ có thể sao chép chúng, bất kể lớp MyClass của chúng.