Tại sao phương thức clone () được bảo vệ trong java.lang.Object?


112

Lý do cụ thể clone()được xác định là được bảo vệ trong là java.lang.Objectgì?

Câu trả lời:


107

Thực tế là bản sao được bảo vệ là vô cùng đáng ngờ - cũng như thực tế là clonephương thức không được khai báo trong Cloneablegiao 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 Cloneablebâ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 Cloneablenhư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 "


2
Nếu đối tượng không thể sao chép, thì bản sao của đối tượng () sẽ ném ra CloneNotSupportedException. Vì vậy, bạn cần phải Cloneable nếu bạn định gọi super.clone () (dẫn đến Object.clone () được gọi). Tôi không thấy làm thế nào một đối tượng có thể được tuần tự hóa mà không triển khai Serializable.
Steve Kuo

1
"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." [cần trích dẫn]
Kevin Panko

Xin lỗi - tôi không ám chỉ điều đó. Tôi chỉ ngụ ý rằng thiết kế "tốt" không phải là mở rộng giao diện 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ó!
oxbow_lakes,

6
@Kevin - Java pp45 hiệu quả của Josh Bloch . "Giao diện có thể nhân bản được dự định như một giao diện mixin cho các đối tượng quảng cáo rằng chúng cho phép sao chép. Thật không may, nó không phục vụ mục đích này"
oxbow_lakes

Cũng trên trang này: "Đây là cách sử dụng giao diện 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 đối với một lớp, nó và tất cả các lớp cha của nó phải tuân theo một quy tắc khá phức tạp, giao thức không thể thực thi và phần lớn không có tài liệu "
oxbow_lakes

30

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.)


Đó là tốt, cho đến khi bạn mang giao diện vào trộn - thử và cố gắng sao chép một thực hiện không rõ củaSet
oxbow_lakes

@oxbow_lakes: nhưng có lẽ một số hiện thực của Set không cloneable
newacct

3
Bạn không thể sao chép bất cứ thứ gì không triển khai giao diện Clonable - đó là một điểm đánh dấu cho biết "Lớp này có thể sao chép đúng cách" - rất giống với giao diện Serializable. Nhân tiện, có một cách để sao chép các lớp thông qua tuần tự hóa hoạt động tốt - hãy google một cái gì đó như "java serialization clone" và có thể bạn sẽ tìm thấy một số cách để có được bản sao sâu đối tượng của mình.
Hóa đơn K

4
Bạn không thể sao chép bất cứ thứ gì không triển khai giao diện Có thể sao chép, nhưng chỉ vì một cái gì đó triển khai giao diện Có thể sao chép không có nghĩa là bạn có thể sao chép nó.
Michael Myers

1
@BuckCherry toString có một triển khai mặc định, nếu bạn gọi nó là điều gì đó tốt sẽ xảy ra và bạn sẽ nhận lại một chuỗi. bằng có một triển khai mặc định (giống như ==). Sao chép không thể có triển khai mặc định. Nếu bạn gọi bản sao trên một đối tượng chưa triển khai nó, bạn sẽ không có được hành vi hợp lý. Sao chép rất phức tạp và không thể thực sự được thực hiện tự động (một số đối tượng không có hàm tạo mặc định có thể không thể sao chép chung), vì vậy theo mặc định, chúng làm cho nó an toàn hơn một chút. Tuy nhiên, tôi nghĩ rằng việc đưa nó lên Object có thể không cần thiết.
Bill K

7

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 clonephươ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ó.


nhưng tại sao nó phải được bảo vệ cho điều đó?
Janusz

3
Nó được bảo vệ để bạn không sử dụng cái trong đối tượng (dù sao thì nó cũng chỉ ném ra một ngoại lệ). Họ muốn bạn ghi đè nó trong một lớp, sau đó bạn đặt nó ở chế độ công khai. (trả lời một vài lần dưới đây quá)
Bill K

1
Và vô dụng khi xem xét các giao diện theo quan điểm của tôi dưới đây
oxbow_lakes

đáng lẽ phải ghi đè là không rõ ràng lắm thì lẽ ra nó phải là trừu tượng và sau đó không phải là trong Lớp đối tượng, tôi đang cố gắng hết sức để hiểu tại sao lại như vậy.
Kumar Abhishek

4

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.


2

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.


Trên thực tế việc thực hiện mặc định (trong đối tượng) ném một ngoại lệ theo các tài liệu ...
Bill K

2
Không, nó không chỉ ném. Từ JavaDoc của nó: "Phương thức sao chép đối với lớp Đối tượng thực hiện một hoạt động sao chép cụ thể. Đầu tiên, nếu lớp của đối tượng này không triển khai giao diện Có thể sao chép, thì một ngoại lệ CloneNotSupportedException sẽ được đưa ra. Lưu ý rằng tất cả các mảng được coi là triển khai giao diện Có thể sao chép. Nếu không, phương thức này tạo một phiên bản mới của lớp đối tượng này và khởi tạo tất cả các trường của nó với chính xác nội dung của các trường tương ứng của đối tượng này, như thể bằng cách gán; nội dung của các trường không được sao chép. "
Pavel Minaev

2

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.


2
Bạn không thể gọi bản sao trên mọi Đối tượng, vì nó được bảo vệ!
Pavel Minaev

2

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
  • #clonephải được gọi bởi các lớp con ob Objecttriển khai Cloneable để có được bản sao nông của lớp phù hợp

Phạ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?

protected.

Các lớp thực hiện Cloneabletấ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.


0

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.


-2

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.


1
CloneNotSupportedException là một ví dụ khác về một ngoại lệ đã kiểm tra cần được bỏ chọn (nghĩa là nó nên mở rộng RuntimeException, không phải Exception). Mặc dù phương thức clone () trong lớp Side thực hiện Cloneable và do đó sẽ không bao giờ ném CloneNotSupportedException, Side.clone () vẫn phải bắt hoặc khai báo ngoại lệ. Điều này chỉ thêm nhiễu xử lý ngoại lệ thừa vào phương thức clone ().
Derek Mahar

-2

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 ()


-3

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ì:

  1. 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.

  2. Nếu bạn có Myclass triển khai Cloneable => bạn ghi đè clone () bằng "public MyClass clone ()"

  3. 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.


2
nếu lớp của bạn tình cờ được kế thừa việc triển khai bản sao lớp dẫn xuất của bạn sẽ không an toàn cho đến khi việc triển khai lớp cơ sở của bạn cung cấp các phương thức sao chép an toàn. Ngoài ra, nó là một loại điều kiện thiết kế bất thường để có một giao diện không có phương thức / thuộc tính. Giao diện này không bắt buộc lớp thực hiện bản sao.
prap19
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.