2015-12-06 38 views
10

Cloneable trong Java vốn đã bị hỏng. Cụ thể, vấn đề lớn nhất của tôi với giao diện là nó mong đợi một hành vi của phương thức mà không định nghĩa chính phương thức đó. Vì vậy, nếu duyệt qua danh sách Cloneable bạn phải sử dụng phản chiếu để truy cập hành vi được xác định của nó. Tuy nhiên, trong Java 8, bây giờ chúng ta có các phương thức mặc định và bây giờ tôi hỏi tại sao không có phương thức mặc định là clone() trong Cloneable.Tại sao không có bản sao mặc định() trong Cloneable trong Java 8

Tôi hiểu lý do tại sao interfaces cannot default Object methods, tuy nhiên, đây là một quyết định thiết kế rõ ràng và do đó có thể có ngoại lệ.

tôi loại hình dung ti Object.clone() và thay đổi mã nội thất của nó để cái gì đó như:

if(this instanceof Cloneable) { 
    return ((Cloneable) this).clone(); 
} 
else { 
    throw new CloneNotSupportedException(); 
} 

Và di chuyển trên bất cứ điều gì kỳ diệu làm cho clone() làm điều này như một phương pháp mặc định trong Cloneable. Điều này không thực sự khắc phục rằng clone() vẫn có thể dễ dàng được triển khai không chính xác, nhưng đó là một cuộc thảo luận khác của chính nó.

Theo như tôi vẫn có thể thay đổi này sẽ là hoàn toàn tương thích ngược:

  1. Lớp học hiện ghi đè clone() nhưng đã không thực hiện Cloneable (TẠI SAO ?!) vẫn sẽ được kỹ thuật ổn (ngay cả khi chức năng không thể , nhưng điều này không khác gì trước đó).
  2. Các lớp hiện đang ghi đè clone(), nhưng đã triển khai Cloneable sẽ vẫn hoạt động tương tự khi triển khai.
  3. Các lớp hiện không ghi đè clone() nhưng đã triển khai Cloneable (TẠI SAO ?!) giờ đây sẽ tuân thủ đặc điểm kỹ thuật, ngay cả khi không phải là hoàn toàn chính xác.
  4. Những người đã sử dụng phản ánh và được gọi là Object.clone() vẫn hoạt động bình thường.
  5. super.clone() sẽ vẫn có chức năng giống nhau ngay cả khi nó tham chiếu Object.clone().

Chưa kể điều này sẽ giải quyết một vấn đề lớn mà Cloneable là. Trong khi tẻ nhạt và vẫn dễ thực hiện không chính xác, nó sẽ giải quyết một vấn đề hướng đối tượng lớn với giao diện.

Vấn đề duy nhất tôi có thể thấy với điều này là những vấn đề triển khai Cloneable không bắt buộc phải ghi đè clone(), nhưng điều này không khác gì so với trước đây.

Điều này đã được thảo luận trong nội bộ, nhưng chưa bao giờ kết quả? Nếu vậy, tại sao? Nếu đó là lý do giao diện không thể mặc định đối tượng phương pháp, nó sẽ không có ý nghĩa để làm cho một ngoại lệ trong trường hợp này vì tất cả các đối tượng kế thừa Cloneable đang mong đợi clone() anyway?

+5

Tôi nhớ đọc ở đâu đó (từ các nguồn chính thức) mà 'Cloneable' đã bị hỏng nên nó thậm chí còn không đáng để sửa chữa. Nhưng một trong những vấn đề với phương pháp mặc định của bạn là sử dụng 'this' là khó khăn ở đó. – Tunaki

+5

Về cơ bản, những gì @Tunaki nói là chính xác. Sự trở lại-trên-phức tạp cho loại nhảy hoop này chỉ là không có; chúng tôi đã chọn đầu tư ngân sách {thời gian, nỗ lực, phức tạp} ở các khu vực khác mang lại nhiều giá trị hơn. –

+1

Không có điểm nào trong việc tạo phương thức 'mặc định' như vậy. Nếu có một phương thức trong 'java.lang.Object' và phương thức' default' có cùng tên và chữ ký trong một 'giao diện', phương thức được khai báo trong' java.lang.Object' vẫn thắng cho các lời gọi không đủ tiêu chuẩn. Và nếu phương thức trong 'java.lang.Object' vẫn' bảo vệ', tất cả các triển khai 'Cloneable' được thực thi để redeclare phương thức là' public', trong khi thay đổi nó thành 'public' vẫn sẽ yêu cầu thích ứng với các hiện thực hiện có. Nói cách khác, kết quả không khác với việc định nghĩa phương thức 'abstract clone()' trong giao diện 'Cloneable'. – Holger

Trả lời

6

Câu hỏi của bạn có phần mở rộng và thảo luận nhiều hơn, nhưng tôi có thể làm sáng tỏ vấn đề này.

Trong Hiệu quả Java ™, Joshua Bloch cung cấp khá nhiều tóm tắt về tình hình.Anh mở ra với một chút lịch sử đằng sau Cloneable

Giao diện Cloneable được dùng làm giao diện mixin cho các đối tượng quảng cáo cho phép nhân bản. Thật không may, nó không phục vụ mục đích này. Lỗ hổng chính của nó là nó thiếu phương pháp sao chép, và phương pháp sao chép của Object được bảo vệ. Bạn không thể, mà không dùng đến sự phản chiếu, gọi phương thức nhân bản trên một đối tượng chỉ vì nó thực hiện Cloneable.

và tiếp tục với các lập luận

[Cloneable] xác định hành vi thực hiện bản sao bảo vệ đối tượng của: nếu một lớp học thực hiện Cloneable, phương pháp nhân bản đối tượng trả về một lĩnh vực-by-field bản sao của đối tượng. .. Đây là một cách sử dụng không điển hình của giao diện và không phải là một trong những được mô phỏng. Thông thường, việc thực hiện một giao diện nói điều gì đó về những gì một lớp có thể làm cho các máy khách của nó. Trong trường hợp Cloneable, nó thay đổi hành vi của một phương thức được bảo vệ trên một lớp cha.

Nếu thực hiện giao diện Cloneable là phải có bất kỳ tác dụng trên một lớp, lớp và tất cả các superclasses của nó phải tuân theo một giao thức mỏng ghi lại khá phức tạp, không thể thực thi, và . Cơ chế kết quả là extralinguistic: nó tạo ra một đối tượng mà không cần gọi một hàm tạo.

Có rất nhiều chi tiết mà đi sâu vào điều này, nhưng cần lưu ý chỉ là một vấn đề:

Kiến trúc clone là không phù hợp với việc sử dụng bình thường của các lĩnh vực chính thức đề cập đến đối tượng có thể thay đổi.

Tôi nghĩ điều này là đủ để lý do chống lại việc có phương thức default trong giao diện thực hiện sao chép. Nó sẽ vô cùng phức tạp để thực hiện nó một cách chính xác.

5

Trải nghiệm của tôi có lẽ không chính thống, nhưng tôi sử dụng clone() và hỗ trợ thiết kế hiện tại là Cloneable. Có lẽ sẽ tốt hơn nếu có chú thích thay thế, nhưng Cloneable xuất hiện lâu trước các chú thích. Ý kiến ​​của tôi là Cloneable là một điều cấp thấp và không ai nên làm điều gì đó như obj instanceof Cloneable. Nếu bạn đang sử dụng Cloneable trong một số logic nghiệp vụ, tốt hơn hết là khai báo giao diện hoặc lớp trừu tượng của riêng bạn để hiển thị clone() cho công chúng và triển khai nó trong tất cả các đối tượng logic nghiệp vụ của bạn. Đôi khi bạn có thể không muốn để lộ clone() thực sự, nhưng tạo ra phương pháp của riêng bạn trong đó sử dụng clone() nội bộ.

Ví dụ, hãy xem xét rằng bạn có một hệ thống phân cấp các đối tượng được đặt tên mà tên không thể thay đổi sau khi xây dựng, nhưng bạn muốn cho phép nhân bản chúng với tên mới. Bạn có thể tạo một số lớp trừu tượng như thế này:

public abstract class NamedObject implements Cloneable { 
    private String name; 

    protected NamedObject(String name) { 
     this.name = name; 
    } 

    public final String getName() { 
     return name; 
    } 

    public NamedObject clone(String newName) { 
     try { 
      NamedObject clone = (NamedObject)super.clone(); 
      clone.name = newName; 
      return clone; 
     } 
     catch(CloneNotSupportedException ex) { 
      throw new AssertionError(); 
     } 
    } 
} 

đây ngay cả khi bạn thực hiện Cloneable, bạn muốn sử dụng clone(), nhưng không muốn để lộ công khai. Thay vào đó, bạn cung cấp một phương thức khác cho phép sao chép với một tên khác. Vì vậy, việc có công khai clone() trong số Cloneable sẽ gây ô nhiễm không cần thiết cho giao diện công khai của các lớp học của bạn.

Một trường hợp khác mà tôi sử dụng Cloneable là việc triển khai Spliterator.trySplit(). Xem implementation của bộ tách đơn giản, trả về số đối tượng cố định nhất định. Nó có bốn chuyên môn (cho các đối tượng, ints, longs và double), nhưng nhờ có clone() tôi chỉ có thể thực hiện trySplit() chỉ một lần trong lớp cha. Một lần nữa, tôi không muốn để lộ clone(), tôi chỉ muốn sử dụng nó một mình.

Vì vậy, để kết luận, không có phương thức clone() trong giao diện Cloneable thực sự linh hoạt hơn vì nó cho phép tôi quyết định có muốn công khai hay không.

+0

@ Holger, đó là một phương pháp khác có tham số không giống 'clone được bảo vệ()' –

Các vấn đề liên quan