Nội dung bài viết
I. Định nghĩa
Trước khi đi vào định nghĩa, nhắc lại 1 số kiến thức chung:
- Java có 2 kiểu dữ liệu (data type): primitive và reference.
- Khi chúng ta viết 1 class ví dụ
public class Person
có nghĩa là chúng ta đã tạo ra 1 data type mới (có tên làPerson
) và nó thuộc vào loại reference.
Generics
nghĩa là linh động, linh hoạt, tùy vào điều kiện A để làm việc X, tùy vào điều kiện B để làm việc Y. Đại khái là không cố định.
Generics
trong Java là parameterized types
, có nghĩa là bạn có thể tạo ra 1 class/interface/method mà có kiểu dữ liệu (data type) linh động và có thể truyền từ ngoài vào như parameter vậy. Khi đó, class/interface/method sẽ được gọi là generics class, generics method.
II. Mục đích
1. Vấn đề
Đôi khi chúng ta viết 1 class hoặc 1 biến mà mục đích là 1 dạng template để phục vụ cho nhiều type khác nhau, chúng ta có xu hướng chọn type là Object
. Ví dụ:
private Object id;
id
có thể là String, Integer hoặc bất kỳ reference type nào
Vấn đề chính là kiểu viết này là bạn phải chấp nhận việc explicit casting
và như thế là bạn mất type safety
.
- Nếu bạn không biết
cast
thì cast là việc bạn chuyển đổi kiểu dữ liệu, vd từ Object sang String hoặc Object sang Integer.
- Okay, nếu bạn ko biết Type Safety thì nó là việc chương trình chỉ cho phép bạn thực hiện 1 hành động mà hành động đó là valid. Ví dụ: nếu object là String thì bạn có thể gọi
substring()
, nếu object là Integer thì bạn ko thể gọi đượcsubstring()
Bạn sẽ phải cast trước rồi mới gọi method như sau:
((String) id).subString(index); hoặc ((Integer) id).intValue();
Và rất có thể bạn sẽ gặp phải Runtime Exception: ClassCastException.
2. cách giải quyết bằng generics
Thay vì việc sử dụng Object
để làm type chung thì bạn có sử dụng Type Parameter để định nghĩa cho biến đó.
private T id;
rồi bạn cho biết T
là gì trước khi gọi vào các method subString
hay intValue
. Điều tuyệt vời là bạn sẽ không bao giờ bị ClassCastException. Nếu bạn đã xác định T
là String, mà bạn lại assign Integer cho id
thì sẽ bị lỗi compile luôn.
Chốt lại: Generics giúp bạn “đẩy” lỗi từ Runtime (lúc program chạy) sang lúc compile time (lúc code), tìm bug sớm hơn, lúc chạy ít lỗi hơn.
3. Ví dụ trong thực tế
Code không có generics
List numbers = new ArrayList(); numbers.add(123); numbers.add("abc"); for (int i = 0; i < numbers.size(); i++) { if ((Integer) numbers.get(i) > 10) //cast here System.out.println("greater than 10"); } /*Exception in thread "main" java.lang.ClassCastException: class java.lang.String cannot be cast to class java.lang.Integer (java.lang.String and java.lang.Integer */
Vì phần tử thứ 2 trong list numbers
là String, nên sẽ bị lỗi ClassCastException lúc run code.
Code có generics
List<Integer> numbers = new ArrayList<>(); numbers.add(123); numbers.add("abc"); //compile error for (int i = 0; i < numbers.size(); i++) { if (numbers.get(i) > 10) //no cast System.out.println("greater than 10"); }
List Interger mà lại add String vào, generics sẽ thực hiện type checking
và phát hiện ra sai type ở dòng số 3 -> Compile error luôn ở dòng số 3, không cần phải chạy mới biết có bug.
III. Tổng kết
Bài này chủ yếu nói về mục đích và ý tưởng của java generics, các bài sau mình sẽ hướng dẫn cách sử dụng trong thực tế.