# Bài 3: Lambda Expressions và Method References

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

* Giúp người đọc hiểu và sử dụng thành thạo **lambda expressions** và **method references** trong Java 8.
    
* Giải thích cú pháp, cách sử dụng, và so sánh hiệu năng giữa lambda và method references.
    
* Cung cấp ví dụ minh họa chi tiết để áp dụng vào thực tế.
    

---

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

#### **1\. Lambda Expressions**

Lambda expressions là một trong những tính năng quan trọng nhất của Java 8, giúp viết code ngắn gọn và dễ đọc hơn bằng cách thay thế các lớp vô danh (anonymous classes).

1. **Cú pháp cơ bản:**
    
    * Cú pháp chung: `(parameters) -> { body }`.
        
    * Nếu chỉ có một tham số và một biểu thức đơn giản, có thể bỏ qua dấu ngoặc và dấu ngoặc nhọn:
        
        ```java
        (a, b) -> a + b; // Trả về tổng của a và b
        x -> x * x;      // Trả về bình phương của x
        () -> System.out.println("Hello"); // Không có tham số
        ```
        
    * Ví dụ:
        
        ```java
        Runnable r = () -> System.out.println("Hello, World!");
        r.run(); // In ra "Hello, World!"
        ```
        
2. **Ví dụ minh họa:**
    
    * Sử dụng lambda với `Comparator`:
        
        ```java
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
        names.sort((s1, s2) -> s1.compareTo(s2));
        System.out.println(names); // [Alice, Bob, Charlie]
        ```
        
    * Sử dụng lambda với `Predicate`:
        
        ```java
        Predicate<Integer> isEven = num -> num % 2 == 0;
        System.out.println(isEven.test(4)); // true
        ```
        
3. **Lợi ích của lambda:**
    
    * Giảm boilerplate code.
        
    * Code dễ đọc và dễ bảo trì hơn.
        
    * Hỗ trợ lập trình hàm (functional programming).
        

---

#### **2\. Method References**

Method references là một cách ngắn gọn để viết lambda expressions khi lambda chỉ gọi một phương thức đã có sẵn.

1. **Bốn dạng method references:**
    
    * **Tham chiếu đến phương thức tĩnh (static method):**
        
        ```java
        Function<String, Integer> parseInt = Integer::parseInt;
        System.out.println(parseInt.apply("123")); // 123
        ```
        
    * **Tham chiếu đến phương thức của một đối tượng cụ thể (instance method):**
        
        ```java
        List<String> names = Arrays.asList("Alice", "Bob");
        names.forEach(System.out::println); // In từng phần tử
        ```
        
    * **Tham chiếu đến phương thức của một đối tượng tùy ý (instance method của một lớp):**
        
        ```java
        Function<String, String> toUpperCase = String::toUpperCase;
        System.out.println(toUpperCase.apply("hello")); // HELLO
        ```
        
    * **Tham chiếu đến constructor:**
        
        ```java
        Supplier<List<String>> listSupplier = ArrayList::new;
        List<String> list = listSupplier.get();
        ```
        
2. **Ví dụ minh họa:**
    
    * Sử dụng method reference với `Comparator`:
        
        ```java
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
        names.sort(String::compareToIgnoreCase);
        System.out.println(names); // [Alice, Bob, Charlie]
        ```
        
    * Sử dụng method reference với `Function`:
        
        ```java
        Function<String, Integer> stringLength = String::length;
        System.out.println(stringLength.apply("Java")); // 4
        ```
        
3. **Lợi ích của method references:**
    
    * Code ngắn gọn hơn so với lambda.
        
    * Tái sử dụng các phương thức đã có sẵn.
        

---

#### **3\. So sánh lambda và method references**

1. **Hiệu năng:**
    
    * Cả lambda và method references đều có hiệu năng tương đương nhau vì chúng đều được biên dịch thành các lớp vô danh (anonymous classes) hoặc sử dụng `invokedynamic`.
        
    * Tuy nhiên, method references có thể tối ưu hơn trong một số trường hợp vì chúng tái sử dụng phương thức đã có sẵn.
        
2. **Cú pháp:**
    
    * Lambda linh hoạt hơn vì có thể chứa nhiều dòng code và logic phức tạp.
        
    * Method references ngắn gọn hơn nhưng chỉ phù hợp khi lambda chỉ gọi một phương thức đã có sẵn.
        
3. **Khi nào sử dụng:**
    
    * **Lambda:** Khi cần thực hiện logic phức tạp hoặc không có phương thức sẵn có để tham chiếu.
        
    * **Method references:** Khi chỉ cần gọi một phương thức đã có sẵn và muốn code ngắn gọn hơn.
        

---

### **Ví dụ tổng hợp**

1. **Sử dụng lambda và method references trong Stream:**
    
    ```java
    List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
    
    // Sử dụng lambda
    names.stream()
         .filter(name -> name.length() > 3)
         .forEach(name -> System.out.println(name));
    
    // Sử dụng method references
    names.stream()
         .filter(name -> name.length() > 3)
         .forEach(System.out::println);
    ```
    
2. **Sử dụng constructor reference:**
    
    ```java
    Supplier<List<String>> listSupplier = ArrayList::new;
    List<String> list = listSupplier.get();
    list.add("Java");
    System.out.println(list); // [Java]
    ```
    

---

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

* **Lambda expressions** giúp viết code ngắn gọn và linh hoạt hơn, đặc biệt khi làm việc với Functional Interface.
    
* **Method references** là cách ngắn gọn để tham chiếu đến các phương thức đã có sẵn, giúp code dễ đọc hơn.
    
* Tùy vào tình huống cụ thể, bạn có thể chọn sử dụng lambda hoặc method references để tối ưu hóa code.
    

---

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

1. Viết một lambda expression để sắp xếp một danh sách các số nguyên theo thứ tự giảm dần.
    
2. Sử dụng method reference để in ra độ dài của từng chuỗi trong một danh sách.
    
3. Viết một lambda expression để tạo một danh sách mới chứa bình phương của các số nguyên từ một danh sách cho trước.
    

---

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

1. **Sắp xếp danh sách:**
    
    ```java
    List<Integer> numbers = Arrays.asList(5, 3, 1, 4, 2);
    numbers.sort((a, b) -> b.compareTo(a));
    System.out.println(numbers); // [5, 4, 3, 2, 1]
    ```
    
2. **In độ dài chuỗi:**
    
    ```java
    List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
    names.stream()
         .map(String::length)
         .forEach(System.out::println); // 5, 3, 7
    ```
    
3. **Tạo danh sách bình phương:**
    
    ```java
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
    List<Integer> squares = numbers.stream()
                                   .map(x -> x * x)
                                   .collect(Collectors.toList());
    System.out.println(squares); // [1, 4, 9, 16]
    ```
    

---

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

Bài viết này đã hướng dẫn chi tiết cách sử dụng **lambda expressions** và **method references** trong Java 8. Hiểu rõ cách kết hợp các tính năng này giúp bạn viết code ngắn gọn, dễ đọc và tận dụng tối đa sức mạnh của Functional Interface.
