Generics trong java với ví dụ cụ thể

Cho method fromArrayToList(Integer[] a) romArrayToList(Integer[] a) dùng để chuyển một array sang ArrayList.

public static List fromArrayToList(Integer[] a) {
        return Arrays.stream(a).collect(Collectors.toList());
}

Bây giờ, nếu mình muốn chuyển một array String, float, double etc thì làm sao nhỉ? Chúng ta sẽ đi với thêm các method tương tự chỉ đổi parameter thành kiểu String, double, float tương ứng. 

Chúng ta sẽ làm việc nêu trên nếu JDK của chúng ta là version nhỏ hơn 5.0. Từ bản JDK 5.0 java generics được giới thiệu cho phép các method, class, interface nhận tham số là các kiểu dữ liệu khác, tăng tính tái sử dụng và chất lượng code.

Generic method

Generic method là những method được viết để có thể được gọi các các đối số có kiểu dữ liệu khác nhau. Generic method có các chuẩn cần tuân thủ sau:

  • Generic method có một kiểu dữ liệu tham số đầu vào được khai báo trước kiểu dữ liệu trả về của generic method.
  • Kiểu dữ liệu tham số đầu vào có thể được giới hạn (Ví dụ extends Number – Các tham số đầu vào là các kiểu dữ liệu chỉ thuộc Number: Interger, double etc).
  • Method body của generic method được viết như các method bình thường.

Ví dụ: chuyển một array sang ArrayList

public <T> List<T> fromArrayToList(T[] a) {   
    return Arrays.stream(a).collect(Collectors.toList());
}

<T>: chữ ký với ngụ ý rằng method này sử dụng generic với kiểu dữ liệu là T.

Chúng ta có thể có nhiều kiểu dữ liệu generic trong method bằng cách thêm và trong phần chữ ký của method. Ví dụ chuyển một array có type T sang ArrayList có type là S.

public static <T, S> List<S> fromArrayToList(T[] a, Function<T, S> mapperFunction) {
        return Arrays.stream(a)
                .map(mapperFunction)
                .collect(Collectors.toList());
}

Chuyển một array số nguyên sang ArrayList String.

import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

public class Main {

    public static void main(String[] args) {
        Integer[] arr = {1, 2, 3, 4, 5};
        List<String> arrList = fromArrayToList(arr, t -> String.valueOf(t));
        System.out.println(arrList);
    }

    public static <T, S> List<S> fromArrayToList(T[] a, Function<T, S> mapperFunction) {
        return Arrays.stream(a)
                .map(mapperFunction)
                .collect(Collectors.toList());
    }
}

Output: [“1”, “2”, “3”, “4”, “5”]

Bounded Generics

Như đã đề cập ở trên, mình có thể giới hiệu Generic type trong một vùng dữ liệu xác định.

Ví dụ: Mình cần cần chuyển một array các số như integer, double, float, thuộc Number sang ArrayList.

 

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class Main {

    public static void main(String[] args) {
        Integer[] arr = {1, 2, 3, 4, 5};
        List<Integer> arrList = fromArrayToList(arr);
        System.out.println(arrList);
    }

    public static <T extends Number> List<T> fromArrayToList(T[] a) {
        return Arrays.stream(a).collect(Collectors.toList());
    }
}

Output: [1, 2, 3, 4, 5]

<T extends Number>: giới hạn T phải là kiểu dữ liệu con của Number như Interger, double etc.

Multiple Bounds

Chúng ta có thể có nhiều giới hạn rộng generic type 

<T extends Number & Comparable>

Generic class

Generic được khai báo như class thông thường, chỉ khác là tên của class theo sau là <Generic type>

 

// File Box.java 
public class Box<T> {
    private T t;

    public void set(T t) {
        this.t = t;
    }

    public T get() {
        return t;
    }

    public static void main(String[] args) {
        Box<Integer> integerBox = new Box<Integer>();
        Box<String> stringBox = new Box<String>();

        integerBox.set(new Integer(10));
        stringBox.set(new String("https://shareprogramming.net/"));

        System.out.println("Integer Value: " + integerBox.get());
        System.out.println("String Value: " + stringBox.get());
    }
}

Output:

Integer Value: 10
String Value: https://shareprogramming.net/

Generic với kiểu dữ liệu nguyên thuỷ

Một điểm quan trọng là các kiểu dữ liệu nguyên thuỷ không được phép trong Generic.

Ví dụ dưới đây sẽ không thể biên dịch được:

List<int> list = new ArrayList<>();
list.add(17);

Để hiểu tại sao kiểu dữ liệu nguyên thuỷ không được phép, thì lưu ý rằng Generic được biên dịch tại compile-time nghĩa là các kiểu dữ liệu truyền vào được xoá bỏ và các generic type được thực hiện dưới dạng đối tượng Object.

Ví dụ: method add(E e) của List interface

boolean add(E e);

Sẽ được biên dịch thành 

boolean add(Object e);

Vì thế, các kiểu dữ liệu phải được chuyển đổi sang Object, thế nhưng các kiểu dữ liệu nguyên thuỷ không extends từ Object nên chúng không được phép trong generic.

Để sử dụng các kiểu số nguyên, double, chúng ta chỉ còn cách sử dụng các Wrapper class được java cung cấp sẵn như Integer, Double etc.

 

‹Previous Next›

 

4.5 2 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x