Tùy chọn là gì?
Tham số Tùy chọn được sử dụng để mang các đối tượng và cho phép các API khác nhau xử lý các tham chiếu null . Hãy xem đoạn mã:Coffee coffee = new Coffee();
Integer quantity = coffee.getSugar().getQuantity();
Chúng ta có một instance Coffee trong đó chúng ta lấy một ít đường từ một instance của đối tượng Sugar . Nếu chúng ta giả sử rằng giá trị số lượng chưa bao giờ được đặt trong hàm tạo Coffee thì Coffee.getSugar().getQuantity() sẽ trả về NullPointerException . Tất nhiên, chúng ta luôn có thể sử dụng các biện pháp kiểm tra null cũ để khắc phục sự cố.
Coffee coffee = new Coffee();
Integer quantity = 0;
if (coffee.getSugar() != null) {
quantity = coffee.getSugar().getQuantity();
}
Bây giờ mọi thứ có vẻ ổn. Nhưng khi viết mã Java, tốt hơn hết chúng ta nên tránh thực hiện kiểm tra null . Hãy xem cách thực hiện việc này bằng cách sử dụng Tùy chọn.
Cách tạo Tùy chọn
Có ba cách để tạo các đối tượng Tùy chọn:-
of(T value) - khởi tạo một đối tượng không null tùy chọn. Xin lưu ý rằng việc sử dụng of() để chỉ một đối tượng null sẽ tạo ra ngoại lệ NullPointerException .
-
ofNullable(T value) - tạo một giá trị Tùy chọn cho một đối tượng có thể là null.
-
trống() - Tạo một phiên bản Tùy chọn đại diện cho một tham chiếu đến null .
// пример использования Optional.of(T Value)
String name = "foo";
Optional<String> stringExample = Optional.of(name)
// пример использования Optional.ofNullable(T Value)
Integer age = null;
Optional<Integer> integerExample= Optional.ofNullable(age)
// пример использования Optional.empty()
Optional<Object> emptyExample = Optional.empty();
Vậy là bạn có một đối tượng Tùy chọn. Bây giờ chúng ta hãy xem hai phương pháp chính cho Tùy chọn:
-
isPresent() - Phương thức này cho bạn biết liệu đối tượng Tùy chọn có chứa giá trị khác null hay không.
-
get() - Lấy giá trị cho Tùy chọn với giá trị hiện tại. Xin lưu ý rằng việc gọi get() trên một Tùy chọn trống sẽ dẫn đến NullPointerException .
Cải thiện việc kiểm tra Null bằng tùy chọn
Vậy làm thế nào chúng ta có thể cải thiện đoạn mã trên? Với Tùy chọn, chúng ta có thể hiểu sự hiện diện của một đối tượng bằng cách sử dụng isPresent() và truy xuất nó bằng get() . Hãy bắt đầu bằng cách đóng gói kết quả của Coffee.getSugar() với Tùy chọn và sử dụng phương thức isPresent() . Điều này sẽ giúp chúng tôi xác định xem getSugar() có trả về null hay không.Coffee coffee = new Coffee();
Optional<String> sugar = Optional.ofNullable(coffee.getSugar());
int quantity = 0;
if (sugar.isPresent()) {
Sugar sugar = sugar.get();
int quantity = sugar.getQuantity();
}
Nhìn vào ví dụ này, việc đóng gói kết quả của Coffee.getSugar() vào Tùy chọn dường như không thêm bất kỳ giá trị nào mà chỉ gây thêm rắc rối. Chúng ta có thể cải thiện kết quả bằng cách sử dụng những hàm mà tôi cho là yêu thích của mình từ lớp Tùy chọn:
-
map(Function<? super T,?extend U> mapper) - Ánh xạ giá trị có trong Tùy chọn vào hàm được cung cấp. Nếu tham số Tùy chọn trống thì map() sẽ trả về Tùy chọn.empty() .
-
orElse(T other) là phiên bản "đặc biệt" của phương thức get() . Nó có thể nhận giá trị có trong Tùy chọn. Tuy nhiên, trong trường hợp Tùy chọn trống, điều này sẽ trả về giá trị được truyền cho phương thức orElse() .
Coffee coffee = new Coffee();
Integer quantity = Optional.ofNullable(coffee.getSugar())
.map(it -> it.getQuantity())
.orElse(0);
Điều này thực sự tuyệt vời - ít nhất là tôi nghĩ vậy. Bây giờ, nếu trong trường hợp giá trị trống, chúng ta không muốn trả về giá trị mặc định thì chúng ta cần đưa ra một số loại ngoại lệ. orElseThrow(Supplier<? mở rộng X> ngoại lệSupplier) trả về giá trị có trong các tham số Tùy chọn hoặc ném ra một ngoại lệ nếu Tùy chọn trống.
Coffee coffee = new Coffee();
Integer quantity = Optional.ofNullable(coffee.getSugar())
.map(it -> it.getQuantity())
.orElseThrow(IllegalArgumentException::new);
Như bạn có thể thấy, Tùy chọn cung cấp một số lợi thế:
- tóm tắt kiểm tra null
- cung cấp API để xử lý các đối tượng null
- cho phép cách tiếp cận khai báo diễn đạt những gì đang đạt được
Cách trở nên hiệu quả với Tùy chọn
Trong công việc của mình, tôi sử dụng Tùy chọn làm kiểu trả về khi một phương thức có thể trả về trạng thái "không có kết quả". Tôi thường sử dụng nó khi xác định kiểu trả về cho các phương thức.Optional<Coffee> findByName(String name) {
...
}
Đôi khi điều này là không cần thiết. Ví dụ: nếu tôi có một phương thức trả về int , chẳng hạn như getQuantity() trong lớp Sugar thì phương thức đó có thể trả về 0 nếu kết quả là null để biểu thị "không có số lượng". Bây giờ, khi biết điều này, chúng ta có thể nghĩ rằng tham số Sugar trong lớp Coffee có thể được biểu diễn dưới dạng Tùy chọn. Thoạt nhìn, đây có vẻ là một ý tưởng hay vì về mặt lý thuyết, đường không nhất thiết phải có trong cà phê. Tuy nhiên, đây là lúc tôi muốn giải quyết khi không sử dụng Tùy chọn. Chúng ta nên tránh sử dụng Tùy chọn trong các trường hợp sau:
-
Là loại tham số cho POJO , chẳng hạn như DTO . Các tùy chọn không thể tuần tự hóa được, vì vậy việc sử dụng chúng trong POJO sẽ khiến đối tượng không thể tuần tự hóa được.
-
Là một đối số phương pháp. Nếu một đối số của phương thức có thể là null thì từ góc độ mã thuần túy, việc chuyển null vẫn được ưu tiên hơn là chuyển Tùy chọn. Ngoài ra, bạn có thể tạo các phương thức nạp chồng để xử lý một cách trừu tượng sự vắng mặt của đối số phương thức null.
-
Để thể hiện một đối tượng Collection bị thiếu. Bộ sưu tập có thể trống, do đó, Bộ sưu tập trống, chẳng hạn như Bộ hoặc Danh sách trống , phải được sử dụng để thể hiện Bộ sưu tập không có giá trị.
GO TO FULL VERSION