Sử dụng requireNonNull()như các câu lệnh đầu tiên trong một phương thức cho phép xác định ngay / nhanh nguyên nhân của ngoại lệ.
Ngăn xếp chỉ ra rõ ràng rằng ngoại lệ đã được ném ngay khi nhập phương thức vì người gọi không tôn trọng các yêu cầu / hợp đồng.
Truyền một nullđối tượng sang một phương thức khác thực sự có thể gây ra một ngoại lệ tại một thời điểm nhưng nguyên nhân của vấn đề có thể phức tạp hơn để hiểu vì ngoại lệ sẽ được đưa vào một lời gọi cụ thể trên nullđối tượng có thể xa hơn.
Dưới đây là một ví dụ cụ thể và thực tế cho thấy lý do tại sao chúng ta phải ưu tiên thất bại nhanh nói chung và đặc biệt hơn là sử dụng Object.requireNonNull()hoặc bất kỳ cách nào để thực hiện kiểm tra không null đối với các tham số được thiết kế là không null.
Giả sử một Dictionarylớp mà soạn một LookupServicevà một Listsố Stringđại diện từ chứa trong. Những trường này được thiết kế để không nullvà một trong những được truyền trong Dictionary constructor.
Bây giờ, giả sử một triển khai "xấu" Dictionarymà không nullkiểm tra trong mục nhập phương thức (ở đây là hàm tạo):
public class Dictionary {
private final List<String> words;
private final LookupService lookupService;
public Dictionary(List<String> words) {
this.words = this.words;
this.lookupService = new LookupService(words);
}
public boolean isFirstElement(String userData) {
return lookupService.isFirstElement(userData);
}
}
public class LookupService {
List<String> words;
public LookupService(List<String> words) {
this.words = words;
}
public boolean isFirstElement(String userData) {
return words.get(0).contains(userData);
}
}
Bây giờ, hãy gọi hàm Dictionarytạo với nulltham chiếu cho wordstham số:
Dictionary dictionary = new Dictionary(null);
// exception thrown lately : only in the next statement
boolean isFirstElement = dictionary.isFirstElement("anyThing");
JVM ném NPE vào câu lệnh này:
return words.get(0).contains(userData);
Ngoại lệ trong luồng "chính" java.lang.NullPulumException
tại Tra cứu dịch vụ.isFirstE bổ sung (Tra cứu dịch vụ.java)
tại Dictionary.isFirstEuity (Dictionary.java:15)
tại Dictionary.main (Dictionary.java:22)
Ngoại lệ được kích hoạt trong LookupServicelớp trong khi nguồn gốc của nó sớm hơn (hàm Dictionarytạo). Nó làm cho phân tích vấn đề tổng thể ít rõ ràng hơn nhiều.
Là words null? Là words.get(0) null? Cả hai? Tại sao cái này, cái kia hoặc có thể cả hai null? Đây có phải là một lỗi mã hóa trong Dictionary(phương thức khởi tạo? Phương thức được gọi không?)? Có phải là một lỗi mã hóa trong LookupService? (phương thức khởi tạo? gọi phương thức?)?
Cuối cùng, chúng ta sẽ phải kiểm tra thêm mã để tìm nguồn gốc lỗi và trong một lớp phức tạp hơn, thậm chí có thể sử dụng trình gỡ lỗi để hiểu dễ dàng hơn những gì nó đã xảy ra.
Nhưng tại sao một điều đơn giản (thiếu kiểm tra null) lại trở thành một vấn đề phức tạp?
Bởi vì chúng tôi cho phép lỗi ban đầu / thiếu nhận dạng trên một rò rỉ thành phần cụ thể trên các thành phần thấp hơn.
Hãy tưởng tượng rằng đó LookupServicekhông phải là một dịch vụ cục bộ mà là một dịch vụ từ xa hoặc thư viện của bên thứ ba với một vài thông tin gỡ lỗi hoặc tưởng tượng rằng bạn không có 2 lớp mà là 4 hoặc 5 lớp yêu cầu đối tượng trước khi nullphát hiện ra? Vấn đề sẽ vẫn phức tạp hơn để phân tích.
Vì vậy, cách để ủng hộ là:
public Dictionary(List<String> words) {
this.words = Objects.requireNonNull(words);
this.lookupService = new LookupService(words);
}
Theo cách này, không phải đau đầu: chúng tôi nhận được ngoại lệ ngay khi nhận được:
// exception thrown early : in the constructor
Dictionary dictionary = new Dictionary(null);
// we never arrive here
boolean isFirstElement = dictionary.isFirstElement("anyThing");
Ngoại lệ trong luồng "chính" java.lang.NullPulumException
tại java.util.Objects.requireNonNull (Object.java:203)
tại com.Dixi. (Dictionary.java:15)
tại com.Dixi.main (Dictionary.java:24)
Lưu ý rằng ở đây tôi đã minh họa vấn đề với một hàm tạo nhưng một lời gọi phương thức có thể có cùng một ràng buộc kiểm tra không null.