2013-02-20 44 views
25

Tôi đã đọc Effective Java bởi Joshua Bloch.Tính tương thích nhị phân trong Java là gì?

Trong mục 17: "Chỉ sử dụng giao diện để xác định loại", tôi đi qua phần giải thích ở nơi không nên sử dụng Giao diện để lưu trữ hằng số. Tôi đang đưa ra lời giải thích dưới đây.

"Tồi tệ hơn, nó thể hiện một cam kết: nếu trong một thông cáo tương lai lớp được sửa đổi để nó không còn nhu cầu sử dụng các hằng số, nó vẫn phải thực hiện giao diện để đảm bảo khả năng tương thích nhị phân ."

Tính tương thích nhị phân có ý nghĩa gì ở đây?

Ai đó có thể hướng dẫn tôi bằng một ví dụ trong Java để hiển thị mã đó tương thích nhị phân.

+0

http: // docs.oracle.com/javase/specs/jls/se7/html/jls-13.html –

+0

tham chiếu không thể thiếu khi xem xét các thay đổi có khả năng phá vỡ tính tương thích nhị phân: https: //wiki.eclipse.org/Evolving_Java-based_APIs_2 – jordanpg

Trả lời

24

Trong ngắn hạn, khả năng tương thích nhị phân nghĩa là khi bạn thay đổi lớp học, bạn không cần phải biên dịch lại các lớp sử dụng nó. Ví dụ: bạn đã xóa hoặc đổi tên một phương thức công khai hoặc được bảo vệ khỏi lớp học này

public class Logger implements Constants { 
    public Logger getLogger(String name) { 
     return LogManager.getLogger(name); 
    } 
} 

từ thư viện log-1.jar của bạn và phát hành phiên bản mới dưới dạng log-2.jar. Khi người dùng log-1.jar của bạn tải xuống phiên bản mới, nó sẽ phá vỡ ứng dụng của họ khi họ sẽ cố gắng sử dụng phương thức getLogger (String name) bị thiếu.

Và nếu bạn loại bỏ giao diện Constants (mục 17) điều này sẽ phá vỡ khả năng tương thích nhị phân, hoặc do cùng một lý do.

Nhưng bạn có thể xóa/đổi tên thành viên riêng tư hoặc gói riêng tư của lớp này mà không vi phạm khả năng tương thích nhị phân, vì ứng dụng bên ngoài không thể (hoặc không nên sử dụng).

+0

Điều làm tôi ngạc nhiên là tài liệu chính thức không thể giải thích nội dung của họ với mức độ đơn giản này. Tôi không chắc chắn lý do tại sao họ muốn sử dụng một luật sư ngôn ngữ tương tự thường sử dụng trong điều khoản và thỏa thuận công cụ. – Tarik

+2

Richard E. Little có một ví dụ rất đơn giản, đẹp mắt trên [blog] của anh ấy (http://codefhtagn.blogspot.kr/2010/11/java-binary-compatibility-more-than.html). Đó là ** thực sự ** cho thấy vấn đề tương thích nhị phân hơn là khả năng tương thích mã nguồn, như được minh họa trong câu trả lời này (với phương thức đã đổi tên). –

-1

Để làm cho mọi chuyện có vẻ đơn giản:

Một máy tính có thể chạy mã nhị phân cùng dự định sẽ được chạy trên máy tính khác được cho là nhị phân tương thích. Điều này khác với khả năng tương thích mã nguồn, nơi có thể cần biên dịch lại.

Tính tương thích nhị phân là một lợi ích lớn khi phát triển các chương trình máy tính được chạy trên nhiều hệ điều hành.

+0

Tôi tin rằng những gì bạn đang nói không phải là khía cạnh tương tự như OP đã yêu cầu –

+0

Câu hỏi là về khả năng tương thích nhị phân trong Java. Bạn vừa sao chép và dán một phần định nghĩa của Wikipedia về 'tính tương thích nhị phân'. – sversch

6

khả năng tương thích nhị phân

Java tương thích nhị phân quy định điều kiện mà modication và tái lập các lớp học thực hiện không đòi hỏi phải tái lập các lớp học thêm khẩu ing các lớp modied. Khả năng tương thích nhị phân là khái niệm tiểu thuyết mới cho thiết kế ngôn ngữ.

Các Java language specication [7] mô tả tạp thay đổi điều chỉnh phù nhị phân như sau:

Một sự thay đổi đến một loại tương thích nhị phân với (tương đương, không phá vỡ tính tương thích với) binaries tồn tại trước đó nếu trước các tệp nhị phân hiện có mà trước đó được liên kết mà không có lỗi sẽ con- tinue để liên kết mà không có lỗi.

13

Để hiểu rõ hơn khái niệm, thật thú vị khi thấy rằng khả năng tương thích nhị phân KHÔNG ngụ ý khả năng tương thích API và ngược lại.

API tương thích tương thích nhưng KHÔNG nhị phân: loại bỏ tĩnh

Phiên bản 1 của thư viện: Mã

public class Lib { 
    public static final int i = 1; 
} 

Chủ đầu tư:

public class Main { 
    public static void main(String[] args) { 
     if ((new Lib()).i != 1) throw null; 
    } 
} 

Compile mã khách hàng với phiên bản 1:

javac Main.java 

Thay thế phiên bản 1 với phiên bản 2: loại bỏ static:

public class Lib { 
    public final int i = 1; 
} 

biên dịch lại chỉ phiên bản 2, không mã khách hàng, và chạy java Main:

javac Lib.java 
java Main 

Chúng tôi nhận được:

Exception in thread "main" java.lang.IncompatibleClassChangeError: Expected static field Lib.i 
     at Main.main(Main.java:3) 

Điều này xảy ra bởi vì mặc dù chúng tôi có thể viết (new Lib()).i trong Java cho cả hai phương pháp static và thành viên, nó biên dịch thành hai hướng dẫn VM khác nhau tùy thuộc vào Lib: getstatic hoặc getfield. phá vỡ này được nêu tại JLS 7 13.4.10:

Nếu một lĩnh vực mà không được khai báo là private đã không khai báo tĩnh và được thay đổi để được khai báo tĩnh, hoặc ngược lại, sau đó một lỗi liên kết, đặc biệt là một IncompatibleClassChangeError, sẽ cho kết quả nếu trường được sử dụng bởi một nhị phân đã tồn tại trước đó, dự kiến ​​một trường của loại khác.

Chúng tôi cần biên dịch lại Main với javac Main.java để nó hoạt động với phiên bản mới.

Ghi chú:

  • gọi các thành viên tĩnh từ trường lớp như (new Lib()).i là phong cách xấu, đặt ra một cảnh báo, và không bao giờ nên được thực hiện
  • ví dụ này là giả tạo vì không tĩnh final nguyên thủy là vô dụng: luôn luôn sử dụng static final cho nguyên thủy: private final static attribute vs private final attribute
  • phản ánh có thể được sử dụng to see the difference. Nhưng sự phản chiếu cũng có thể thấy các trường riêng tư, điều này rõ ràng dẫn đến việc phá vỡ mà không được tính là ngắt, vì vậy nó không được tính.

Binary tương thích nhưng KHÔNG API tương thích: null trước tình trạng tăng cường

Phiên bản 1:

public class Lib { 
    /** o can be null */ 
    public static void method(Object o) { 
     if (o != null) o.hashCode(); 
    } 
} 

Version 2:

public class Lib { 
    /** o cannot be null */ 
    public static void method(Object o) { 
     o.hashCode(); 
    } 
} 

Chủ đầu tư:

public class Main { 
    public static void main(String[] args) { 
     Lib.method(null); 
    } 
} 

Lần này, ngay cả khi biên dịch lại Main sau khi cập nhật Lib, lệnh gọi thứ hai sẽ ném, nhưng không phải lần đầu tiên.

Điều này là do chúng tôi đã thay đổi hợp đồng method theo cách không thể kiểm tra tại thời gian biên dịch bằng Java: trước khi có thể mất null, sau khi không còn nữa.

Ghi chú:

  • wiki Eclipse là một nguồn tuyệt vời cho việc này: https://wiki.eclipse.org/Evolving_Java-based_APIs
  • làm API mà chấp nhận null giá trị là một thực tế đáng ngờ
  • nó là dễ dàng hơn nhiều để thực hiện một sự thay đổi mà phá vỡ API khả năng tương thích nhưng không nhị phân hơn ngược lại, vì dễ dàng thay đổi logic nội bộ của các phương thức
+0

IMHO ví dụ đầu tiên là không chính xác, bởi vì bạn loại bỏ 'tĩnh' và nó bây giờ là không tương thích API (ví dụ,' Lib.i' trong bytecode không hoạt động nữa). Để lý do khả năng tương thích API cho một số mã không phụ thuộc vào việc khách hàng sử dụng API này nhưng tiêu chuẩn (hoặc đặc tả) cho khả năng tương thích API. Mặc dù tôi đồng ý rằng khả năng tương thích API không ngụ ý khả năng tương thích nhị phân. – floating

0

Nếu trong tương lai, chúng tôi muốn thay đổi e giao diện mà một số lớp đang triển khai (ví dụ: thêm một số phương thức mới).

Nếu chúng ta thêm các phương pháp trừu tượng (bổ sung phương pháp), sau đó các lớp (thực hiện giao diện) phải thực hiện các phương pháp bổ sung tạo hạn chế sự phụ thuộc và chi phí chi phí để thực hiện giống nhau.

Để khắc phục điều này, chúng tôi có thể thêm các phương thức mặc định trong giao diện.

Điều này sẽ loại bỏ sự phụ thuộc để triển khai các phương thức bổ sung.

Chúng tôi không cần sửa đổi lớp triển khai để kết hợp các thay đổi. Điều này được gọi là Khả năng tương thích nhị phân.

Vui lòng tham khảo ví dụ dưới đây:

Các giao diện mà chúng ta sẽ sử dụng

//Interface  
    interface SampleInterface 
      { 
       // abstract method 
       public void abstractMethod(int side); 

       // default method 
       default void defaultMethod() { 
        System.out.println("Default Method Block"); 
       } 

       // static method 
       static void staticMethod() { 
        System.out.println("Static Method Block"); 
       } 
      } 


//The Class that implements the above interface. 

    class SampleClass implements SampleInterface 
    { 
     /* implementation of abstractMethod abstract method, if not implemented 
     will throw compiler error. */ 
     public void abstractMethod(int side) 
     {System.out.println(side*side);} 

     public static void main(String args[]) 
     { 
      SampleClass sc = new SampleClass(); 
      sc.abstractMethod(4); 

      // default method executed 
      sc.defaultMethod(); 

      // Static method executed 
      SampleInterface.staticMethod(); 

     } 
    } 

Lưu ý: Để biết thông tin chi tiết hơn, vui lòng tham khảo default methods

+0

Các phương thức mặc định và tĩnh có sẵn trong Java phiên bản 8 trở lên. –

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