Ở phần trước, các bạn đã được biết đến Generics trong Java, nhưng nó vẫn chưa thực sự linh động.
Thử với ví dụ thường gặp mỗi khi nhắc đến lập trình hướng đối tượng đó là:
Ta có 2 class Student, Teacher. Cho 1 danh sách gồm sinh viên và giáo viên, in ra thông tin người tuổi lớn nhất trong danh sách.
Quá quen thuộc, ta sẽ tạo 1 abstract Person làm lớp cha, và cho 2 lớp trên kế thừa lớp cha này.

package net.tunghuynh.generic.wildcard;

public abstract class Person {
    protected String name;
    protected int age;
    protected String address;

    public Person(String name, int age, String address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    public abstract void say();
}
package net.tunghuynh.generic.wildcard;

public class Student extends Person {
    public Student(String name, int age, String address) {
        super(name, age, address);
    }

    @Override
    public void say() {
        System.out.println("I'm a Student");
    }
}
package net.tunghuynh.generic.wildcard;

public class Teacher extends Person {
    public Teacher(String name, int age, String address) {
        super(name, age, address);
    }

    @Override
    public void say() {
        System.out.println("I'm a Teacher");
    }
}

Cuối cùng là test xử lý xem kết quả ra sao.

package net.tunghuynh.generic;

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List < Person > lstPerson = new ArrayList < > ();
        lstPerson.add(new Student("Nam", 21, "Thai Nguyen"));
        lstPerson.add(new Teacher("Trung", 40, "Ha Noi"));
        findMaxAge(lstPerson);

    }
    public static void findMaxAge(List < Person > lstPerson) {
        Person maxAge = null;
        for (Person p: lstPerson) {
            if (maxAge == null || maxAge.age < p.age) {
                maxAge = p;
            }
        }
        if (maxAge != null) {
            showInfor(maxAge);
        }
    }
    public static void showInfor(Person p) {
        System.out.println(String.format("%s, %d years old, from %s", p.name, p.age, p.address));
        p.say();
    }
}

Output:

Trung, 40 years old, from Ha Noi
I'm a Teacher

Có vẻ rất ổn đúng ko nào, đạt được yêu cầu. Nhưng thực tế thì mặc dù lớp Student và Teacher được extend từ Person, nhưng không phải lúc nào cũng có thể tạo 1 List Person để xử lý cho các method dùng chung. Giả sử bạn truy vấn được 1 List<Student> từ database lên nhưng cũng không thể ép kiểu nó cho về List<Person> để xử lý.
Ta hoàn toàn có thể truyền 1 đối tượng của lớp Student hoặc Teacher vào method showInfor() nhưng lại không thể truyền trực tiếp List<Student> hay List<Teacher> vào method findMaxAge() ở trên vì lỗi sai kiểu.

Đây là lúc cần biết đến Wildcard. Kỹ thuật này giúp ta có thể truyền 1 collection của lớp con vào collection lớp cha theo cách giống như method showInfor(). Cài đặt kỹ thuật này rất đơn giản bằng cách sửa method findMaxAge() như sau

public static void findMaxAge(List<? extends Person> lstPerson){
// todo
}

Giờ thì hoàn toàn có thể truyền List<Student> hoặc List<Teacher> để xử lý.

public static void main(String[] args) {
    List<Student> lstStudent = new ArrayList<>();
    lstStudent.add(new Student("Nam", 21, "Thai Nguyen"));
    lstStudent.add(new Student("Mai", 20, "Nam Dinh"));
    findMaxAge(lstStudent); // OK

    List<Teacher> lstTeacher = new ArrayList<>();
    lstTeacher.add(new Teacher("Trung", 40, "Ha Noi"));
    lstTeacher.add(new Teacher("Thanh", 35, "Ha Noi"));
    findMaxAge(lstTeacher); // OK

    // findMaxAge(lstPerson); // OK
}

Output:

Nam, 21 years old, from Thai Nguyen
I'm a Student
Trung, 40 years old, from Ha Noi
I'm a Teacher

Tất nhiên bạn cũng có thể truyền List<Person> vào method trên mà không gặp lỗi gì 🙂

Đôi khi có những con người ngược đời, có những tình huống ngược đời, có 1 method với tham số là List<Teacher> nhưng lại muốn truyền List<Person> vào thì sao nhỉ. Rất may là trong Wildcard lại cũng hỗ trợ tình huống này bằng từ khóa super theo cách tương tự như extend bên trên

public static void checkTeacher(List<? super Teacher> lstTeacher){
// todo
}
List<Person> lstPerson = new ArrayList<>();
checkTeacher(lstPerson); // OK
checkTeacher(lstTeacher); // OK
checkTeacher(lstStudent); // NOK

Nhưng trong thực tế thì rất ít khi và hầu như là không gặp đến case ngược đời này.

Chúc các bạn ứng dụng được trong các tình huống thực tế, giúp hệ thống có tính mở rộng cao.

2 Replies to “[Java] Phần 3: Cách sử dụng Wildcard trong java”

  1. 2 câu này viết thiếu phải ko Huynh :)))

    Giả sử bạn truy vấn được 1 List từ database lên nhưng cũng không thể ép kiểu nó cho về List để xử lý.
    => Giả sử bạn truy vấn được 1 List “Student” từ database lên nhưng cũng không thể ép kiểu nó cho về List để xử lý.
    không thể truyền trực tiếp List hay List và0 method findMaxAge() ở trên vì lỗi sai kiểu.
    => không thể truyền trực tiếp List “Student” hay List “Teacher” vào method findMaxAge() ở trên vì lỗi sai kiểu.

    1. Ồ. Tks Địa. Hôm nọ a đổi cái template view syntax code cho dễ nhìn nên nó bị hiểu mấy cái <> là tag của html nên xoá sạch. Thành ra bị sai nhiều quá =))). Dạo này rảnh thế ah mà vào ngó của a :v

Bình luận