Để 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
http: // docs.oracle.com/javase/specs/jls/se7/html/jls-13.html –
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