Giới Thiệu
Trong bài trước, chúng ta đã làm quen với Consumer, giao diện hàm cho việc thực hiện các hành động.
Hôm nay, chúng ta sẽ tập trung vào Supplier, giao diện đơn giản nhưng mạnh mẽ nhất cho việc tạo giá trị lười biếng.
Nếu Predicate quyết định, Function biến đổi, và Consumer thực hiện, thì Supplier là nguồn cung. Nó cung cấp giá trị theo yêu cầu, mà không cần đầu vào.
Supplier Là Gì?
Đây là định nghĩa:
java
@FunctionalInterface
public interface Supplier<T> {
T get();
}
- Đầu vào: Không có.
- Đầu ra: Một đối tượng loại T.
- Mục đích: Tạo ra hoặc cung cấp giá trị, thường là lười biếng hoặc lặp lại.
Tại Sao Nên Sử Dụng Supplier?
Truyền thống, các giá trị được sinh ra theo cách ngay lập tức:
java
String token = UUID.randomUUID().toString();
Với Supplier, chúng ta có thể trì hoãn thực hiện và đóng gói việc tạo giá trị:
java
Supplier<String> tokenSupplier = () -> UUID.randomUUID().toString();
System.out.println(tokenSupplier.get()); // Một token mới cho mỗi lần gọi
Điều này đặc biệt mạnh mẽ trong các tình huống đánh giá lười biếng, tải cấu hình, kiểm thử và tiêm phụ thuộc.
Ví Dụ Thực Tiễn
1. Giá Trị Ngẫu Nhiên hoặc Độc Nhất
Suppliers bộc lộ sự ngẫu nhiên hoặc tạo ra giá trị độc nhất một cách tự nhiên:
java
Supplier<Integer> randomInt = () -> new Random().nextInt(100);
System.out.println(randomInt.get()); // ví dụ: 42
System.out.println(randomInt.get()); // ví dụ: 17
2. Khởi Tạo Hoãn
Trì hoãn tính toán tốn kém cho đến khi cần:
java
Supplier<List<User>> heavyQuery = () -> database.fetchAllUsers();
// Chưa có gì xảy ra
List<User> users = heavyQuery.get(); // Truy vấn chỉ được thực hiện tại đây
3. Nhà Máy và Tạo Đối Tượng
Suppliers là những nhà máy tinh tế:
java
Supplier<User> newUser = () -> new User(UUID.randomUUID().toString());
User user1 = newUser.get();
User user2 = newUser.get();
4. Tích Hợp với Stream
Suppliers làm việc với Stream.generate để tạo ra các chuỗi vô hạn:
java
Supplier<String> uuidSupplier = () -> UUID.randomUUID().toString();
List<String> ids = Stream.generate(uuidSupplier)
.limit(3)
.toList();
System.out.println(ids);
// [550e8400-e29b-41d4-a716-446655440000, ...]
Mô Hình Thế Giới Thực
-
Nhà Cung Cấp Cấu Hình:
Tải cấu hình lười biếng khi được yêu cầu. -
Lưu Trữ với Đánh Giá Lười:
Tính toán một lần, sau đó tái sử dụng. -
Tiêm Phụ Thuộc:
Truyền Suppliers thay vì các đối tượng cụ thể để tạo ra đối tượng linh hoạt. -
Kiểm Thử và Giả Mạo:
Suppliers giúp dễ dàng tiêm dữ liệu giả hoặc dữ liệu mô phỏng.
Thực Hành Tốt Nhất
- Giữ Cho Suppliers Trong Sạch: Tránh các tác động phụ ẩn. Suppliers nên chủ yếu tạo ra giá trị.
- Đặt Tên Rõ Ràng: Sử dụng tên như tokenSupplier, userFactory, configProvider.
- Kết Hợp với Hàm Bậc Cao: Suppliers có thể được truyền vào các phương thức để trì hoãn thực hiện hoặc tùy chỉnh hành vi.
Cạm Bẫy Thường Gặp
- Luồng Vô Tận Không Kiểm Soát:
Stream.generatekhông có giới hạn dẫn đến vòng lặp vô hạn. - Logic Nặng Nề Bên Trong: Đừng giấu các phép toán phức tạp trong
Supplier#get(), nó nên nhẹ và dễ dự đoán. - Tác Động Phụ: Mặc dù có thể, nhưng các suppliers có tác động phụ (ví dụ: gọi mạng) làm mờ trách nhiệm. Thích sự thuần khiết hơn.
Phân Tích Chức Năng
Hãy nghĩ về Supplier như một cái giếng:
- Bạn không cung cấp gì cho nó.
- Mỗi khi bạn rút nước (get()), nó cung cấp cho bạn nước tươi (T).
- Cái giếng có thể sâu, vô hạn, hoặc được lưu trữ, nhưng nó luôn cung cấp.
Kết Luận
Supplier là trình tạo lười biếng của Java chức năng.
Nó đóng gói việc tạo giá trị, hỗ trợ đánh giá lười, và tích hợp liền mạch với các luồng, nhà máy, và chiến lược kiểm thử.
Bằng cách làm chủ Supplier, bạn mở khóa tính toán theo yêu cầu và tạo đối tượng linh hoạt.
Tiếp Theo Là Gì
Trong tập tiếp theo, chúng ta sẽ tìm hiểu về UnaryOperator, một hàm chuyên biệt cho việc biến đổi đầu vào đơn.
Trong khi Function biến đổi từ loại này sang loại khác, UnaryOperator chuyên biệt trong việc biến đổi trong cùng một loại, là một yếu tố nền tảng của sự kết hợp chức năng.