# Bài 2: Các Functional Interface phổ biến trong Java 8

### **Mục tiêu:**

* Giới thiệu chi tiết về các Functional Interface (FI) có sẵn trong Java 8.
    
* Giúp người đọc hiểu cách sử dụng các FI phổ biến như `Predicate`, `Function`, `Consumer`, và `Supplier`.
    
* Phân biệt các FI chuyên biệt và lợi ích của chúng.
    

---

### **Dàn ý chi tiết:**

#### **1\. Nhóm FI cơ bản**

Các Functional Interface cơ bản trong Java 8 được định nghĩa trong gói `java.util.function`. Chúng được thiết kế để hỗ trợ các tác vụ phổ biến như kiểm tra điều kiện, ánh xạ dữ liệu, thực hiện hành động, và cung cấp giá trị.

1. `Predicate<T>`: Kiểm tra điều kiện
    
    * **Định nghĩa:** `Predicate<T>` là một FI có phương thức `test(T t)` trả về giá trị boolean.
        
    * **Công dụng:** Dùng để kiểm tra một điều kiện nào đó trên đối tượng kiểu `T`.
        
    * **Ví dụ:**
        
        ```java
        Predicate<Integer> isEven = num -> num % 2 == 0;
        System.out.println(isEven.test(4)); // true
        System.out.println(isEven.test(5)); // false
        ```
        
    * **Ứng dụng:** Thường dùng trong `filter()` của Stream để lọc dữ liệu.
        
2. `Function<T, R>`: Ánh xạ dữ liệu
    
    * **Định nghĩa:** `Function<T, R>` là một FI có phương thức `apply(T t)` trả về giá trị kiểu `R`.
        
    * **Công dụng:** Dùng để chuyển đổi dữ liệu từ kiểu `T` sang kiểu `R`.
        
    * **Ví dụ:**
        
        ```java
        Function<String, Integer> stringLength = str -> str.length();
        System.out.println(stringLength.apply("Java")); // 4
        ```
        
    * **Ứng dụng:** Thường dùng trong `map()` của Stream để biến đổi dữ liệu.
        
3. `Consumer<T>`: Thực hiện hành động
    
    * **Định nghĩa:** `Consumer<T>` là một FI có phương thức `accept(T t)` không trả về giá trị.
        
    * **Công dụng:** Dùng để thực hiện một hành động trên đối tượng kiểu `T`.
        
    * **Ví dụ:**
        
        ```java
        Consumer<String> printMessage = message -> System.out.println(message);
        printMessage.accept("Hello, World!"); // Hello, World!
        ```
        
    * **Ứng dụng:** Thường dùng trong `forEach()` của Stream để thực hiện hành động trên từng phần tử.
        
4. `Supplier<T>`: Cung cấp giá trị
    
    * **Định nghĩa:** `Supplier<T>` là một FI có phương thức `get()` trả về giá trị kiểu `T`.
        
    * **Công dụng:** Dùng để cung cấp giá trị mà không cần đầu vào.
        
    * **Ví dụ:**
        
        ```java
        Supplier<Double> randomValue = () -> Math.random();
        System.out.println(randomValue.get()); // Giá trị ngẫu nhiên từ 0.0 đến 1.0
        ```
        
    * **Ứng dụng:** Thường dùng để tạo Stream từ một nguồn dữ liệu động.
        

---

#### **2\. Nhóm FI chuyên biệt**

Java 8 cung cấp các FI chuyên biệt để tối ưu hóa hiệu suất khi làm việc với các kiểu dữ liệu nguyên thủy (primitive types) như `int`, `double`, `long`, v.v. Các FI này giúp tránh việc auto-boxing/unboxing, từ đó cải thiện hiệu suất.

1. **Primitive Specialization**
    
    * `IntPredicate`, `DoublePredicate`, `LongPredicate`:
        
        * Tương tự `Predicate<T>` nhưng làm việc trực tiếp với kiểu `int`, `double`, `long`.
            
        * Ví dụ:
            
            ```java
            IntPredicate isPositive = num -> num > 0;
            System.out.println(isPositive.test(10)); // true
            ```
            
    * `IntFunction<R>`, `DoubleFunction<R>`, `LongFunction<R>`:
        
        * Tương tự `Function<T, R>` nhưng làm việc trực tiếp với kiểu `int`, `double`, `long`.
            
        * Ví dụ:
            
            ```java
            IntFunction<String> intToString = num -> "Number: " + num;
            System.out.println(intToString.apply(42)); // Number: 42
            ```
            
    * `IntConsumer`, `DoubleConsumer`, `LongConsumer`:
        
        * Tương tự `Consumer<T>` nhưng làm việc trực tiếp với kiểu `int`, `double`, `long`.
            
        * Ví dụ:
            
            ```java
            IntConsumer printSquare = num -> System.out.println(num * num);
            printSquare.accept(5); // 25
            ```
            
    * `IntSupplier`, `DoubleSupplier`, `LongSupplier`:
        
        * Tương tự `Supplier<T>` nhưng làm việc trực tiếp với kiểu `int`, `double`, `long`.
            
        * Ví dụ:
            
            ```java
            IntSupplier randomInt = () -> (int) (Math.random() * 100);
            System.out.println(randomInt.get()); // Giá trị ngẫu nhiên từ 0 đến 99
            ```
            
2. **Multi-arguments FI**
    
    * `BiPredicate<T, U>`:
        
        * Tương tự `Predicate<T>` nhưng nhận hai đối số.
            
        * Ví dụ:
            
            ```java
            BiPredicate<String, Integer> isLengthEqual = (str, len) -> str.length() == len;
            System.out.println(isLengthEqual.test("Java", 4)); // true
            ```
            
    * `BiFunction<T, U, R>`:
        
        * Tương tự `Function<T, R>` nhưng nhận hai đối số.
            
        * Ví dụ:
            
            ```java
            BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
            System.out.println(add.apply(5, 3)); // 8
            ```
            
    * `BiConsumer<T, U>`:
        
        * Tương tự `Consumer<T>` nhưng nhận hai đối số.
            
        * Ví dụ:
            
            ```java
            BiConsumer<String, Integer> printDetails = (name, age) -> 
                System.out.println(name + " is " + age + " years old.");
            printDetails.accept("Alice", 25); // Alice is 25 years old.
            ```
            

---

#### **3\. So sánh ưu/nhược**

1. **Ưu điểm của Primitive Specialization:**
    
    * Tránh auto-boxing/unboxing, giúp cải thiện hiệu suất.
        
    * Phù hợp khi làm việc với dữ liệu nguyên thủy.
        
2. **Nhược điểm của Primitive Specialization:**
    
    * Chỉ hỗ trợ một số kiểu dữ liệu cụ thể (`int`, `double`, `long`).
        
    * Không linh hoạt như các FI generic (`Predicate<T>`, `Function<T, R>`, v.v.).
        

---

#### **4\. Ví dụ minh họa**

* **Ví dụ 1: Sử dụng** `Predicate` và `Function` trong Stream
    
    ```java
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
    List<Integer> evenNumbers = numbers.stream()
                                       .filter(num -> num % 2 == 0) // Predicate
                                       .map(num -> num * 2)         // Function
                                       .collect(Collectors.toList());
    System.out.println(evenNumbers); // [4, 8]
    ```
    
* **Ví dụ 2: Sử dụng** `Consumer` và `Supplier`
    
    ```java
    Supplier<String> messageSupplier = () -> "Hello, World!";
    Consumer<String> messageConsumer = message -> System.out.println(message);
    messageConsumer.accept(messageSupplier.get()); // Hello, World!
    ```
    

---

### **Tổng kết**

* Các Functional Interface cơ bản như `Predicate`, `Function`, `Consumer`, và `Supplier` là nền tảng quan trọng trong Java 8.
    
* Các FI chuyên biệt như `IntPredicate`, `BiFunction`, v.v. giúp tối ưu hóa hiệu suất và hỗ trợ các tác vụ phức tạp hơn.
    
* Hiểu rõ các FI này giúp viết code ngắn gọn, hiệu quả và tận dụng tối đa sức mạnh của Stream API.
    

---

### **Bài tập thực hành**

1. Tạo một `Predicate<String>` để kiểm tra xem một chuỗi có độ dài lớn hơn 5 không.
    
2. Sử dụng `Function<Integer, String>` để chuyển đổi một số nguyên thành chuỗi có định dạng "Number: X".
    
3. Viết một `Consumer<Integer>` để in ra bình phương của một số.
    
4. Sử dụng `Supplier<LocalDate>` để cung cấp ngày hiện tại.
    

---

### **Gợi ý đáp án**

1. **Predicate:**
    
    ```java
    Predicate<String> isLong = str -> str.length() > 5;
    System.out.println(isLong.test("Java")); // false
    ```
    
2. **Function:**
    
    ```java
    Function<Integer, String> formatNumber = num -> "Number: " + num;
    System.out.println(formatNumber.apply(42)); // Number: 42
    ```
    
3. **Consumer:**
    
    ```java
    Consumer<Integer> printSquare = num -> System.out.println(num * num);
    printSquare.accept(5); // 25
    ```
    
4. **Supplier:**
    
    ```java
    Supplier<LocalDate> todaySupplier = () -> LocalDate.now();
    System.out.println(todaySupplier.get()); // Ngày hiện tại
    ```
    

---

### **Kết luận**

Bài viết này đã giới thiệu chi tiết về các Functional Interface phổ biến trong Java 8, từ cơ bản đến chuyên biệt. Hiểu rõ các FI này giúp bạn viết code hiệu quả hơn và tận dụng tối đa sức mạnh của Stream API trong các bài viết tiếp theo.
