Java generics phần 1 – Goals

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.

Generics in Java - Ensured.io
copy image from https://www.ensured.io/software-development/generics-in-java/

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 được substring()

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ế.

0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments