Trong bài viết này, chúng ta sẽ tìm hiểu về tính tổng quát hóa trong Java, cách tạo các lớp và phương thức tổng quát và những ưu điểm cùng với các ví dụ dẫn chứng.
Tính tổng quát hóa
Tính tổng quát hóa cho phép chúng ta tạo một lớp, giao diện và phương thức duy nhất có thể được sử dụng với nhiều loại dữ liệu hoặc đối tượng khác nhau. Điều này giúp chúng ta có thể tái sử dụng các đoạn mã của mình.
Chú ý: Tính tổng quát hóa sẽ không hoạt động hiệu quả với các kiểu dữ liệu căn bản như int, float, char.
Lớp tổng quát
Chúng ta có thể tạo một lớp có thể được sử dụng với bất kỳ loại dữ liệu nào. Một lớp như vậy được gọi là lớp tổng quát.
Ví dụ:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
class Main { public static void main(String[] args) { lop_A<Integer> a = new lop_A<>(100); System.out.println(a.get()); lop_A<String> b = new lop_A<>("Lap trinh Java"); System.out.println(b.get()); } } class lop_A<T> { private T thuoc_tinh; public lop_A(T thuoc_tinh) { this.thuoc_tinh = thuoc_tinh; } public T get() { return this.thuoc_tinh; } } |
Kết quả:
1 2 |
100 Lap trinh Java |
Trong ví dụ trên, chúng ta đã tạo một lớp tổng quát có tên là lop_A. Lớp này có thể được sử dụng để làm việc với bất kỳ loại dữ liệu nào. Ở đây, T được sử dụng bên trong dấu ngoặc nhọn <> nhằm cho biết kiểu dữ liệu. Bên trong lớp Main, chúng ta đã tạo hai đối tượng là a và b của lớp tổng quát:
- Đối tượng a – T sẽ được thay thế bằng Integer. Và lớp tổng quát lop_A sẽ làm việc với dữ liệu kiểu số nguyên.
- Đối tượng b – T sẽ được thay thế bằng String. Và lớp tổng quát lop_A sẽ làm việc với dữ liệu kiểu chuỗi ký tự.
Các phương thức tổng quát
Tương tự với các lớp tổng quát, chúng ta cũng có thể tạo một phương thức có thể được sử dụng với bất kỳ loại dữ liệu nào. Một phương thức như vậy được gọi là Phương thức tổng quát.
Ví dụ:
1 2 3 4 5 6 7 8 9 10 11 12 |
class Main { public static void main(String[] args) { lop_A a = new lop_A(); a.<String>phuong_thuc_A("Lap trinh Java"); a.<Float>phuong_thuc_A(100f); } } class lop_A { public <T> void phuong_thuc_A(T du_lieu) { System.out.println(du_lieu); } } |
Kết quả:
1 2 |
Lap trinh Java 100.0 |
Trong ví dụ trên, chúng ta đã tạo một phương thức tổng quát có tên là phuong_thuc_A. Ở đây, tham số <T> được chèn vào sau từ public và trước kiểu trả về là void. Chúng ta có thể gọi phương thức tổng quát bằng cách đặt kiểu dữ liệu là <String> và <Float> bên trong dấu ngoặc và trước tên phương thức.
Chú ý: Chúng ta cũng có thể gọi phương thức tổng quát mà không cần thêm tham số cho kiểu dữ liệu.
Ví dụ:
1 |
a.phuong_thuc_A("Lap trinh Java"); |
Trong trường hợp này, trình biên dịch sẽ tự so sánh với tham số cho kiểu dữ liệu dựa trên giá trị được truyền cho phương thức.
Các kiểu dữ liệu bị giới hạn
Tham số cho kiểu dữ liệu có thể chấp nhận bất kỳ kiểu dữ liệu nào (ngoại trừ kiểu dữ liệu căn bản như int, float hay double). Tuy nhiên, nếu chúng ta chỉ muốn sử dụng một số kiểu dữ liệu cụ thể (chẳng hạn như chỉ chấp nhận dữ liệu kiểu số), thì chúng ta có thể sử dụng các kiểu dữ liệu được giới hạn.
Trong trường hợp các kiểu dữ liệu bị ràng buộc, chúng ta sẽ sử dụng từ khóa extends.
Ví dụ:
1 |
<T extends A> |
Điều này có nghĩa là T chỉ có thể chấp nhận kiểu dữ liệu là các kiểu dữ liệu con của A.
1 2 3 4 5 6 7 8 9 10 |
class lop_A <T extends Number> { public void in_thong_tin() { System.out.println("Chi lam viec voi kieu du lieu so!"); } } class Main { public static void main(String[] args) { lop_A<String> a = new lop_A<>(); } } |
Kết quả sẽ trả về là lỗi được thông báo. Trong ví dụ trên, chúng ta đã tạo một lớp có tên là lop_A.
1 |
T extends Number |
Ở đây, lớp tổng quát được khởi tạo với kiểu dữ liệu được giới hạn. Điều này có nghĩa là lớp tổng quát chỉ có thể làm việc với các kiểu dữ liệu là con của Number ví dụ như Integer, Double. Tuy nhiên, chúng ta lại tạo một đối tượng của lớp tổng quát với kiểu dữ liệu là String. Trong trường hợp này, trình biên dịch sẽ thông báo lỗi.
Lợi ích của tính tổng quát hóa trong Java
1. Khả năng tái sử dụng các đoạn mã
Với sự trợ giúp của tính tổng quát hóa trong Java, chúng ta có thể viết đoạn mã mà có thể hoạt động với các loại dữ liệu khác nhau.
Ví dụ:
1 |
public <T> void phuong_thuc(T du_lieu) {...} |
Ở đây, chúng ta đã tạo ra một phương thức tổng quát. Phương thức này có thể được sử dụng lại để thực hiện các thao tác với kiểu dữ liệu số nguyên, dữ liệu kiểu chuỗi ký tự.
2. Kiểm tra kiểu thời gian biên dịch
Tham số kiểu dữ liệu của phương thức tổng quát sẽ cung cấp các thông tin về loại dữ liệu được sử dụng trong đoạn mã tổng quát.
Ví dụ:
1 |
lop_A<Integer> a = new lop_A<>(); |
Ở đây, chúng ta biết rằng lớp tổng quát lop_A chỉ hoạt động với dữ liệu kiểu số nguyên. Bây giờ, nếu chúng ta cố gắng truyền dữ liệu không phải là kiểu số nguyên, chương trình sẽ tạo ra lỗi tại thời điểm biên dịch.
3. Được sử dụng với các tập hợp
Collections Framework sử dụng khái niệm tổng quát trong Java.
Ví dụ:
1 2 |
ArrayList<String> a = new ArrayList<>(); ArrayList<Float> b = new ArrayList<>(); |
Trong ví dụ trên, chúng ta đã sử dụng cùng một lớp ArrayList để làm việc với các kiểu dữ liệu khác nhau. Tương tự như ArrayList, các tập hợp khác như LinkedList, Queue, Maps cũng có tính tổng quát trong Java.
Trên đây là khái niệm và ví dụ cơ bản về tính tổng quát hóa trong Java. Hy vọng mọi người có thể áp dụng vào trong chương trình của mình. Mọi người hãy tiếp tục theo dõi các bài tiếp theo và cập nhật các bài mới nhất trên tek4 nhé!
P/s: Cảm ơn mọi người!