Optional trong Java - làm chủ 15 phương thức trong 20s
Trước khi đi vào bài viết, gửi tới các bạn lời chúc sức khỏe cho một năm mới 2024 thật cháy với ngọn lửa học tập, công việc vừa ý, túi tiền nặng ký nhé ♥️😘
Như các bạn đã biết Java 8 giới thiệu Optional, một công cụ mạnh mẽ giúp xử lý giá trị có thể không tồn tại một cách linh hoạt và an toàn. Điểm mạnh của Optional là khả năng giảm thiểu rủi ro NullPointerException và làm cho mã nguồn trở nên rõ ràng hơn trong việc xử lý các tình huống không dễ dàng.
Trong bài viết này, chúng ta sẽ đào sâu vào thế giới của Optional và tìm hiểu cách làm chủ 15 phương thức quan trọng của nó chỉ trong 20 giây. Từ cơ bản như empty() và of(T value) đến những phương thức mạnh mẽ như map và filter, chúng ta sẽ khám phá cách sử dụng mỗi phương thức cùng với ví dụ thực tế để hiểu rõ hơn về sức mạnh của Optional trong Java.
Nếu bạn muốn nắm bắt khả năng của Optional và trở thành chuyên gia trong việc xử lý giá trị có thể không tồn tại, hãy đồng hành cùng mình trong bài viết này! Bắt đầu ngay bây giờ và làm chủ 15 phương thức quan trọng của Optional chỉ trong chưa đầy 20 giây.
1. Khái quát về Optional
- Khái niệm Trong lập trình Java, Optional là một phần của gói java.util đã được giới thiệu từ phiên bản Java 8. Được thiết kế để giải quyết vấn đề của NullPointerException và làm cho xử lý giá trị có thể không tồn tại trở nên linh hoạt và an toàn hơn.
Optional là một lớp bao bọc, đóng gói một giá trị hoặc không có giá trị. Nó đặt ra khái niệm "optional" (tùy chọn), biểu thị cho sự có hoặc không có của một giá trị và dựa trên giá trị này, nó có thể ở một trong các trạng thái sau:
- present - nếu dữ liệu được gói không phải là null ;
- empty - nếu nó được sử dụng để bao bọc một giá trị null ;
2. Các cách để khởi tạo Optional:
1. Optional.empty()
-
Phương thức này tạo ra một đối tượng Optional trống, không chứa giá trị nào. Nó thường được sử dụng để biểu thị sự vắng mặt của một giá trị.
Optional<String> emptyOptional = Optional.empty();
2. Optional.empty()
-
Phương thức này tạo ra một đối tượng Optional chứa một giá trị không null. Nếu bạn cố gắng truyền null vào Optional.of, nó sẽ ném ra một NullPointerException
Optional<String> optionalWithNonNullValue = Optional.of("Hello");
3. Optional.ofNullable(value)
-
Phương thức này tạo ra một đối tượng Optional từ một giá trị có thể là null. Nếu giá trị đó là null, thì nó trả về một Optional trống; nếu không, nó sẽ tạo ra một Optional chứa giá trị đó.
String nullableString = getNullableString(); // Giả sử hàm này trả về một chuỗi có thể là null Optional<String> optionalFromNullable = Optional.ofNullable(nullableString);
Ghi chú quan trọng:
- Khi sử dụng Optional.of, bạn cần chắc chắn rằng đối số không phải là null để tránh NullPointerException.
- Sử dụng Optional.ofNullable khi bạn không chắc chắn về tính không null của đối số.
- Optional.empty thường được sử dụng trong các phương thức mà bạn muốn rõ ràng trả về một giá trị "không có".
2. Khi nào nên và không nên sử dụng
Điều này đã được mình viết khá chi tiết ở trong bài Optional trong Java - cách sử dụng và khi nào nên sử dụng. Bài viết đó và bài này là hai bài học bổ trợ cho nhau về Optional trong Java, các bạn hãy cố gắng đọc cả hai bài để nắm rõ được các kiến thức quan trọng của Optional nhé
3. 15 phương thức của Optional trong Java
Bây giờ hãy cùng mình đi vào chủ đề chính của bài viết này đó chính là làm chủ 15 phương thức của Optional trong chưa đầy 20s. Hãy cùng mình bấm giờ và chiến nào
Trước khi bắt đầu, chúng ta sẽ có một quy ước nhỏ cho các ví dụ bên dưới: Chúng ta sẽ sử dụng lớp ColorsRepository. Lớp này có một phương thức tĩnh getColor(String colorName) sẽ trả về một gói đối tượng Color được bõ trong một Optional.
Ví dụ: ColorsRepository.getColor(“red”) sẽ trả về một đối tượng Optional và có đối tượng màu “đỏ” bên trong.
Mặt khác, ColorsRepository.getColor(“spencer”) sẽ trả về với một Optional trống vì nó không có màu nào tên “spencer”.
Không dài dòng nữa, bắt đầu ngay nào
1. isPresent()
- Chúng ta có thể sử dụng isPresent() để kiểm tra xem giá trị có tồn tại hay không. Nó sẽ trả về true nếu có và false nếu không
Optional<Color> color = ColorsRepository.getColor("red");
if (color.isPresent()) {
System.out.println("'red' color was found");
}
2. isEmpty()
- Tương tự, chúng ta có thể kiểm tra xem ta có bao bọc một giá trị null hay không bằng cách gọi isEmpty() . Phương thức này ngược lại với isPresent() và nó có thể được sử dụng để mã dễ đọc hơn:
Optional<Color> color = ColorsRepository.getColor("spencer");
if (color.isEmpty()) {
System.out.println("'spencer' color was NOT found");
}
3. get()
- get() có thể được sử dụng để truy cập dữ liệu bên trong Optional. Tuy nhiên, bạn cần cẩn thận, nếu Optional có mặt , thao tác này sẽ trả về dữ liệu mà nó bao bọc — nhưng, nếu Optional trống , việc gọi get() sẽ ném ra NoSuchElementException
Optional<Color> color1 = ColorsRepository.getColor("red");
System.out.println(color1.get()); // Nó sẽ in ra đối tượng màu đỏ
Optional<Color> color2 = ColorsRepository.getColor("spencer");
System.out.println(color2.get()); // Nó sẽ ném ra NoSuchElementExceptionjava
4. orElseThrow()
- orElseThrow() đang thực hiện chính xác điều tương tự như get() - trả về dữ liệu nếu có và ném NoSuchElementException nếu trống.
- Tuy nhiên việc sử dụng get() để ném ra ngoại lệ không được khuyến nghị, bởi đơn giản mục đích của nó không phải để dùng vào việc này. Chính vì thế các nhà phát hành đã tạo ra orElseThrow() - khi bạn muốn ném ra một ngoại lệ nếu giá trị không tồn tại.
Optional<Color> color1 = ColorsRepository.getColor("red");
System.out.println(color1.orElseThrow()); // nó sẽ in ra đối tượng màu đỏ
Optional<Color> color2 = ColorsRepository.getColor("spencer");
System.out.println(color2.orElseThrow()); // nó sẽ ném ra NoSuchElementException
5. orElseThrow(exceptionSupplier)
- Tương tự như trên nhưng lần này bạn sẽ sử dụng Supplier để khai báo một ngoại lệ tùy ý, hoặc cũng có thể một ngoại lệ tùy chỉnh do bạn tự định nghĩa
//Ta hoàn toàn có thể lựa chọn ngoại lệ khác để ném ra nếu muốn
Optional<String> optional = Optional.empty();
try {
String value = optional.orElseThrow(() -> new CustomException("Custom error message"));
System.out.println("Value: " + value);
} catch (CustomException e) {
System.out.println("Caught an exception: " + e.getMessage());
}
- Trong ví dụ này, nếu Optional không chứa giá trị, phương thức orElseThrow sẽ ném một CustomException với thông điệp tùy chỉnh. Bạn có thể thay thế CustomException bằng bất kỳ loại ngoại lệ nào phù hợp với nhu cầu của bạn.
6. orElse(defaultValue)
- Sử dụng khi bạn muốn trả về giá trị mặc định nếu giá trị không tồn tại.
Optional<Color> color1 = ColorsRepository.getColor("red");
System.out.println(color1.orElse(Color.WHITE));
// nó sẽ trả về đối tượng màu đỏ
Optional<Color> color2 = ColorsRepository.getColor("spencer");
System.out.println(color2.orElse(Color.WHITE));
// Nó sẽ trả về đối tượng màu trắng
7. orElseGet(defaultValueSupplier)
-
Phương pháp này rất giống với phương pháp trước. Điểm khác biệt duy nhất là lần này, giá trị mặc định sẽ được lấy thông qua việc thực hiện một "Supplier"
-
Supplier chỉ được thực hiện khi Optional không chứa giá trị.
Optional<Color> color1 = ColorsRepository.getColor("red");
System.out.println(color1.orElse(new Color(150, 150, 200)));
// orElse() will create the new Color, but will print the RED Color object
Optional<Color> color2 = ColorsRepository.getColor("red");
System.out.println(color2.orElseGet(() -> new Color(150, 150, 200)));
// orElseGet() will print the RED Color without creating any other object
Điểm quan trọng là sự khác biệt giữa orElse và orElseGet nằm ở thời điểm việc tạo giá trị mặc định. orElse tạo giá trị mặc định ngay lập tức, trong khi orElseGet cho phép bạn trì hoãn việc tạo giá trị đến khi nó thực sự cần thiết, có thể giúp tối ưu hóa hiệu suất trong một số trường hợp.
8. ifPresent(consumer)
-
Sử dụng khi bạn muốn thực hiện một hành động nếu giá trị tồn tại.
-
ifPresent yêu cầu triển khai Consumer interface làm tham số. Nếu Optional là present , Consumer sẽ được áp dụng, nếu không, nó sẽ bị bỏ qua
Optional<Color> color1 = ColorsRepository.getColor("red");
color1.ifPresent(color -> System.out.println("color retrieved: " + color));
// Nó sẽ in ra message
Optional<Color> color2 = ColorsRepository.getColor("spencer");
color2.ifPresent(color -> System.out.println("color retrieved: " + color));
// Nó sẽ không làm gì cả
9. ifPresentOrElse(consumer, runnable)
- ifPresentOrElse là một phương thức mới được thêm vào lớp Optional trong phiên bản Java 9. Phương thức này được sử dụng để thực hiện một hành động nếu giá trị trong Optional tồn tại, và nếu không, thì thực hiện một hành động khác.
Dưới đây là cú pháp của phương thức ifPresentOrElse:
public void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction)
import java.util.Optional;
public class IfPresentOrElseExample {
public static void main(String[] args) {
Optional<String> optionalWithValue = Optional.of("Hello");
Optional<String> optionalEmpty = Optional.empty();
// Nếu giá trị tồn tại, in ra giá trị, ngược lại in ra một thông báo
optionalWithValue.ifPresentOrElse(
value -> System.out.println("Value is present: " + value),
() -> System.out.println("Value is not present")
);
// Nếu giá trị tồn tại, in ra giá trị, ngược lại in ra một thông báo
optionalEmpty.ifPresentOrElse(
value -> System.out.println("Value is present: " + value),
() -> System.out.println("Value is not present")
);
}
}
Trong ví dụ này, ifPresentOrElse được sử dụng để kiểm tra xem giá trị trong Optional có tồn tại hay không. Nếu giá trị tồn tại, action sẽ được thực hiện (in ra giá trị), ngược lại, emptyAction sẽ được thực hiện (in ra thông báo). Điều này giúp loại bỏ việc phải kiểm tra bằng cách sử dụng isPresent() và giúp làm cho mã nguồn trở nên ngắn gọn hơn.
Lưu ý rằng ifPresentOrElse giúp giảm số lượng kiểm tra và giúp bạn xử lý cả hai trường hợp (có và không giá trị) một cách dễ đọc hơn.
10. filter(filterPredicate)
-
filter nhận đầu vào là một Predicate và trả về một Optional khác. có 3 kịch bản có thể xảy ra:
1. Nếu đối tượng Optional ban đầu trống , filter() cũng sẽ trả về một tùy chọn trống.
Optional<String> emptyOptional = Optional.empty(); Optional<String> filteredOptional = emptyOptional.filter(s -> s.length() > 3);
- Trong trường hợp này, vì giá trị của Optional là "Hello" và độ dài của chuỗi này là lớn hơn 3, filter trả về chính Optional gốc mà không làm thay đổi giá trị.
2. Nếu đối tượng Optional ban đầu có giá trị, Predicate sẽ được đánh giá, nếu điều kiện kiểm tra sai (trả về false), thì filter sẽ trả về một Optional trống
Optional<String> optional = Optional.of("Hi"); Optional<String> filteredOptional = optional.filter(s -> s.length() > 3);
- Trong trường hợp này, vì giá trị của Optional là "Hi" và độ dài của chuỗi này không lớn hơn 3, filter trả về một Optional rỗng.
3. Nếu đối tượng Optional ban đầu có giá trị, Predicate sẽ được đánh giá, nếu điều kiện kiểm tra đúng (trả về true), thì filter sẽ trả về chính Optional gốc mà không làm thay đổi giá trị.
Optional<String> optional = Optional.of("Hello"); Optional<String> filteredOptional = optional.filter(s -> s.length() > 3);
- Trong trường hợp này, vì giá trị của Optional là "Hello" và độ dài của chuỗi này là lớn hơn 3, filter trả về chính Optional gốc mà không làm thay đổi giá trị.
11. map(mappingFunction)
- Sử dụng để ánh xạ giá trị từ một kiểu sang kiểu khác
- map () sẽ nhận một hàm làm tham số và nó sẽ áp dụng hàm đó vào dữ liệu nếu có.
Optional<String> optional = Optional.of("Hello");
Optional<Integer> lengthOptional = optional.map(String::length);
12. FlatMap(mappingFunction)
-
Phương thức này cho phép bạn thực hiện một ánh xạ (mapping) từ giá trị bên trong Optional và trả về một Optional mới. Nó thường được sử dụng khi bạn muốn ánh xạ một giá trị và giữ nguyên kiểu Optional chứa giá trị mới.
-
Dưới đây là cú pháp của phương thức flatMap:
public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper)
- mapper: Một hàm ánh xạ từ giá trị bên trong Optional sang một Optional mới.
-
Ví dụ minh họa:
import java.util.Optional;
public class FlatMapExample {
public static void main(String[] args) {
// Một Optional chứa một chuỗi
Optional<String> originalOptional = Optional.of("Hello");
// Sử dụng flatMap để ánh xạ và giữ nguyên kiểu Optional
Optional<String> resultOptional = originalOptional.flatMap(s -> Optional.of(s.toUpperCase()));
// In ra kết quả
resultOptional.ifPresent(System.out::println); // In ra: HELLO
}
}
-
Trong ví dụ này, flatMap được sử dụng để chuyển đổi giá trị chuỗi bên trong Optional thành một Optional mới chứa chuỗi viết hoa. Kết quả là một Optional mới với giá trị được ánh xạ và giữ nguyên kiểu Optional.
-
flatMap thường được sử dụng khi bạn có một Optional chứa một giá trị và bạn muốn ánh xạ giá trị đó bằng một hàm mà trả về một Optional. Nó giúp giữ nguyên kiểu Optional thay vì nhúng một lớp Optional vào bên trong lớp khác, tạo ra sự linh hoạt trong xử lý kiểu dữ liệu Optional.
13. Optional.ofNullable(nullableObject)
Phương static ofNullable tạo một tùy chọn dựa trên dữ liệu được truyền vào. Nếu dữ liệu là null, nó sẽ trả về một Optional trống. Nếu không, nó sẽ trả về Optional là present và nó sẽ bao bọc dữ liệu được cung cấp.
Optional<String> optional1 = Optional.ofNullable("some random String");
System.out.println(optional1.isPresent());
//Nó sẽ in ra 'true'
Optional<String> optional2 = Optional.ofNullable(null);
System.out.println(optional2.isPresent());
//Nó sẽ in ra 'false' vì optional2 là trống
14. Optional.empty()
- Sử dụng khi bạn muốn tạo ra một Optional rỗng.
- Sử dụng khi bạn muốn biểu thị một giá trị không tồn tại.
Optional<String> emptyOptional = Optional.empty();
15. Optional.of()
- Sử dụng khi bạn chắc chắn giá trị không phải là null.
- Phương thức này chỉ nên được sử dụng cho các giá trị khác null, nếu không, nó sẽ ném ra ngoại lệ NullPointerException:
Optional<String> nonEmptyOptional = Optional.of("Hello");
All rights reserved