0

[Java Backend Zero to Hello] BÀI 2.1: GENERIC

Java Backend Zero to Hello

📚 Bài viết thuộc series Java Backend Zero to Hello 📌 Phần: Phase 2: Java Core Nâng Cao | Bài 16/86


BÀI 2.1: GENERIC

Mục tiêu

  • Hiểu Generic và lợi ích
  • Khai báo và sử dụng Generic class, method, interface
  • Sử dụng Bounded Type Parameters
  • Hiểu Wildcards

1. GENERIC LÀ GÌ?

Generic cho phép viết class/method có thể làm việc với nhiều kiểu dữ liệu nhưng vẫn đảm bảo type safety tại compile time.

Vấn đề không có Generic

// Trước Java 5 - dùng Object
List list = new ArrayList();
list.add("Hello");
list.add(123);  // Lỗi logic nhưng không báo compile

String s = (String) list.get(1);  // ❌ ClassCastException runtime

Với Generic

List<String> list = new ArrayList<>();
list.add("Hello");
// list.add(123);  // ❌ Compile error
String s = list.get(0);  // Không cần cast

2. GENERIC CLASS

public class Box<T> {
    private T content;

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

    public T get() {
        return content;
    }
}

// Sử dụng
Box<String> stringBox = new Box<>();
stringBox.set("Hello");
String s = stringBox.get();

Box<Integer> intBox = new Box<>();
intBox.set(123);
int n = intBox.get();

Quy ước đặt tên Type Parameter

Ký tự Ý nghĩa
T Type
E Element (Collection)
K Key (Map)
V Value (Map)
N Number
S, U, V Type thứ 2, 3, 4

3. GENERIC METHOD

public class Util {
    // Generic method
    public static <T> void swap(T[] array, int i, int j) {
        T temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }

    public static <T> T getFirst(List<T> list) {
        if (list.isEmpty()) {
            throw new IllegalArgumentException("List rỗng");
        }
        return list.get(0);
    }

    public static <K, V> V getOrDefault(Map<K, V> map, K key, V defaultValue) {
        return map.containsKey(key) ? map.get(key) : defaultValue;
    }
}

// Sử dụng
String[] arr = {"A", "B", "C"};
Util.swap(arr, 0, 2);  // [C, B, A]

List<Integer> list = Arrays.asList(1, 2, 3);
Integer first = Util.getFirst(list);  // 1

4. BOUNDED TYPE PARAMETERS

Giới hạn kiểu dữ liệu được phép.

4.1 Upper Bounded - extends

// Chỉ chấp nhận Number và các class con
public class NumberBox<T extends Number> {
    private T number;

    public double doubleValue() {
        return number.doubleValue();
    }
}

NumberBox<Integer> intBox = new NumberBox<>();   // OK
NumberBox<Double> doubleBox = new NumberBox<>();  // OK
// NumberBox<String> strBox = new NumberBox<>();  // ❌ Compile error

4.2 Multiple Bounds

public class MyClass<T extends Number & Comparable<T>> {
    // T phải là Number và implement Comparable
}

MyClass<Integer> obj = new MyClass<>();  // OK
// MyClass<String> obj2 = new MyClass<>();  // ❌

5. WILDCARDS

5.1 Unbounded Wildcard - <?>

public void printList(List<?> list) {
    for (Object item : list) {
        System.out.println(item);
    }
}

printList(Arrays.asList(1, 2, 3));      // OK
printList(Arrays.asList("A", "B"));     // OK

5.2 Upper Bounded Wildcard - <? extends T>

// Chấp nhận List<Integer>, List<Double>, List<Number>
public double sum(List<? extends Number> list) {
    double total = 0;
    for (Number n : list) {
        total += n.doubleValue();
    }
    return total;
}

sum(Arrays.asList(1, 2, 3));           // OK
sum(Arrays.asList(1.5, 2.5));          // OK
// sum(Arrays.asList("A", "B"));       // ❌

5.3 Lower Bounded Wildcard - <? super T>

// Chấp nhận List<Integer>, List<Number>, List<Object>
public void addNumbers(List<? super Integer> list) {
    for (int i = 1; i <= 5; i++) {
        list.add(i);
    }
}

List<Integer> intList = new ArrayList<>();
addNumbers(intList);  // OK

List<Number> numList = new ArrayList<>();
addNumbers(numList);  // OK

5.4 PECS Rule

  • Producer Extends: Dùng ? extends T khi chỉ đọc (lấy ra)
  • Consumer Super: Dùng ? super T khi chỉ ghi (thêm vào)
// Producer - chỉ đọc
public void copy(List<? extends Number> source, List<? super Number> dest) {
    for (Number n : source) {
        dest.add(n);
    }
}

6. TYPE ERASURE

Java Generic chỉ tồn tại ở compile time, bị xóa ở runtime (type erasure).

// Source code
List<String> stringList = new ArrayList<>();
List<Integer> intList = new ArrayList<>();

// Sau khi compile (bytecode)
List stringList = new ArrayList();
List intList = new ArrayList();
// Cả hai đều là List<Object> ở runtime

Hệ quả

  • Không thể new T() trong generic class
  • Không thể tạo mảng generic: new T[10] ❌
  • Không thể dùng instanceof với generic type
public class Box<T> {
    private T value;

    public Box() {
        // value = new T();  // ❌ Compile error
    }

    public boolean isInstance(Object obj) {
        // return obj instanceof T;  // ❌ Compile error
        return obj instanceof Box;  // OK
    }
}

7. GENERIC INTERFACE

public interface Repository<T, ID> {
    T findById(ID id);
    List<T> findAll();
    T save(T entity);
    void deleteById(ID id);
}

public class UserRepository implements Repository<User, Long> {
    @Override
    public User findById(Long id) { /* ... */ return null; }

    @Override
    public List<User> findAll() { /* ... */ return null; }

    @Override
    public User save(User entity) { /* ... */ return null; }

    @Override
    public void deleteById(Long id) { /* ... */ }
}

8. BÀI TẬP THỰC HÀNH

Bài 1: Generic Stack

public class Stack<T> {
    private List<T> elements = new ArrayList<>();

    public void push(T item) {
        elements.add(item);
    }

    public T pop() {
        if (isEmpty()) throw new EmptyStackException();
        return elements.remove(elements.size() - 1);
    }

    public T peek() {
        if (isEmpty()) throw new EmptyStackException();
        return elements.get(elements.size() - 1);
    }

    public boolean isEmpty() {
        return elements.isEmpty();
    }

    public int size() {
        return elements.size();
    }
}

Bài 2: Generic Pair

public class Pair<K, V> {
    private K key;
    private V value;

    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }

    public K getKey() { return key; }
    public V getValue() { return value; }

    public static <K, V> Pair<K, V> of(K key, V value) {
        return new Pair<>(key, value);
    }
}

Pair<String, Integer> pair = Pair.of("Age", 25);

Bài 3: Generic Utility

public class CollectionUtil {
    public static <T> List<T> filter(List<T> list, Predicate<T> predicate) {
        List<T> result = new ArrayList<>();
        for (T item : list) {
            if (predicate.test(item)) {
                result.add(item);
            }
        }
        return result;
    }

    public static <T, R> List<R> map(List<T> list, Function<T, R> mapper) {
        List<R> result = new ArrayList<>();
        for (T item : list) {
            result.add(mapper.apply(item));
        }
        return result;
    }
}

9. TÓM TẮT

Khái niệm Cú pháp Mục đích
Generic class class Box<T> Class làm việc với nhiều kiểu
Generic method <T> void method(T t) Method linh hoạt
Bounded <T extends Number> Giới hạn kiểu
Wildcard <?> Kiểu không xác định
Upper bound <? extends T> Producer
Lower bound <? super T> Consumer
Type erasure - Generic chỉ ở compile time

Bài tiếp theo: 2.2 Lambda Expression


🧭 Điều Hướng Series

⬅️ Bài trước: PHASE 2: JAVA CORE NÂNG CAO - Tổng Quan & Mục Tiêu

📋 Lộ trình tổng quan: Xem Toàn Bộ Series

➡️ Bài tiếp theo: BÀI 2.2: LAMBDA EXPRESSION


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí