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 Dictionary
lớp mà soạn một LookupService
và một List
số String
đại diện từ chứa trong. Những trường này được thiết kế để không null
và một trong những được truyền trong Dictionary
constructor.
Bây giờ, giả sử một triển khai "xấu" Dictionary
mà không null
kiể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 Dictionary
tạo với null
tham chiếu cho words
tham 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 LookupService
lớp trong khi nguồn gốc của nó sớm hơn (hàm Dictionary
tạ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 đó LookupService
khô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 null
phá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.