Generic Programming trong Java

1. Khái Niệm Cơ Bản

1.1 Generic là gì?

  • Định nghĩa:
  • Cho phép viết code có thể tái sử dụng với nhiều kiểu dữ liệu
  • Kiểm tra kiểu tại thời điểm biên dịch
  • Loại bỏ việc ép kiểu thủ công

  • Lợi ích:

  • Type safety: Phát hiện lỗi tại compile-time
  • Code reusability: Tái sử dụng code với nhiều kiểu
  • Performance: Không cần type casting
  • Clean code: Code rõ ràng, dễ đọc

1.2 Cú Pháp Cơ Bản

// 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 Generic");
String content = stringBox.get();

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

2. Generic Methods và Wildcards

2.1 Generic Methods

  • Đặc điểm:
  • Định nghĩa kiểu generic cho method
  • Độc lập với class generic
  • Type inference tự động
public class GenericMethods {
    // Generic method
    public <T> void printArray(T[] array) {
        for (T element : array) {
            System.out.println(element);
        }
    }

    // Generic method với nhiều type parameters
    public <T, U> Pair<T, U> makePair(T first, U second) {
        return new Pair<>(first, second);
    }

    // Generic method với type bounds
    public <T extends Number> double sum(List<T> numbers) {
        double sum = 0.0;
        for (T number : numbers) {
            sum += number.doubleValue();
        }
        return sum;
    }
}

// Sử dụng
GenericMethods gm = new GenericMethods();
String[] strings = {"Hello", "World"};
gm.printArray(strings);

Pair<String, Integer> pair = gm.makePair("Key", 123);

2.2 Wildcards

  • Upper Bounded Wildcards:
  • Sử dụng <? extends Type>
  • Cho phép Type và các lớp con của nó
  • Read-only access

  • Lower Bounded Wildcards:

  • Sử dụng <? super Type>
  • Cho phép Type và các lớp cha của nó
  • Write access

  • Unbounded Wildcards:

  • Sử dụng <?>
  • Cho phép mọi kiểu
  • Sử dụng khi không quan tâm đến kiểu
public class WildcardExample {
    // Upper bounded wildcard
    public double sumOfList(List<? extends Number> list) {
        double sum = 0.0;
        for (Number n : list) {
            sum += n.doubleValue();
        }
        return sum;
    }

    // Lower bounded wildcard
    public void addNumbers(List<? super Integer> list) {
        for (int i = 1; i <= 10; i++) {
            list.add(i);
        }
    }

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

3. Type Bounds và Constraints

3.1 Type Bounds

  • Single Bound:
  • Giới hạn kiểu generic bằng một interface/class
  • Sử dụng từ khóa extends
  • Đảm bảo kiểu có các phương thức cần thiết

  • Multiple Bounds:

  • Giới hạn kiểu bằng nhiều interface
  • Chỉ được extends một class
  • Class phải đứng đầu danh sách bounds
// Single bound
public class NumberBox<T extends Number> {
    private T number;

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

// Multiple bounds
public class DataProcessor<T extends Comparable<T> & Serializable> {
    public void process(T data) {
        // Process data
    }
}

// Sử dụng với interface tự định nghĩa
interface Processable {
    void process();
}

interface Storable {
    void save();
}

public class ProcessableData<T extends Processable & Storable> {
    private T data;

    public void executeAll() {
        data.process();
        data.save();
    }
}

3.2 Type Erasure

  • Cơ chế:
  • Chuyển đổi code generic thành non-generic
  • Thay thế type parameters bằng bounds
  • Thêm type casting khi cần thiết
// Trước type erasure
public class Box<T> {
    private T content;
    public T get() { return content; }
    public void set(T content) { this.content = content; }
}

// Sau type erasure
public class Box {
    private Object content;
    public Object get() { return content; }
    public void set(Object content) { this.content = content; }
}

4. Generic Collections

4.1 Collection Classes

  • List Generic:
  • ArrayList
  • LinkedList
  • Vector

  • Set Generic:

  • HashSet
  • TreeSet
  • LinkedHashSet

  • Map Generic:

  • HashMap
  • TreeMap
  • LinkedHashMap
public class GenericCollections {
    public void demonstrateCollections() {
        // List
        List<String> stringList = new ArrayList<>();
        stringList.add("Hello");
        String first = stringList.get(0);

        // Set
        Set<Integer> numberSet = new HashSet<>();
        numberSet.add(1);
        numberSet.add(2);

        // Map
        Map<String, Integer> scores = new HashMap<>();
        scores.put("John", 95);
        scores.put("Alice", 98);

        // Nested generics
        Map<String, List<Integer>> studentScores = new HashMap<>();
        studentScores.put("John", Arrays.asList(95, 87, 98));
    }
}

4.2 Custom Generic Collections

// Generic Stack
public class Stack<T> {
    private List<T> items = new ArrayList<>();

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

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

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

// 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 void setKey(K key) { this.key = key; }
    public void setValue(V value) { this.value = value; }
}

5. Best Practices và Design Patterns

5.1 Best Practices

  • Nguyên tắc thiết kế:
  • Sử dụng generics cho type safety
  • Ưu tiên bounded wildcards
  • Tránh raw types
  • Đặt tên type parameters có ý nghĩa
public class GenericBestPractices {
    // BAD: Raw type
    List list = new ArrayList();

    // GOOD: Generic type
    List<String> stringList = new ArrayList<>();

    // BAD: Object type
    List<Object> objectList = new ArrayList<>();

    // GOOD: Wildcard
    List<?> unknownList = new ArrayList<>();

    // GOOD: Bounded wildcard
    public void processNumbers(List<? extends Number> numbers) {
        // Process numbers
    }
}

5.2 Common Design Patterns với Generics

// Generic Singleton
public class Singleton<T> {
    private static Singleton<?> instance;
    private final T value;

    private Singleton(T value) {
        this.value = value;
    }

    public static <T> Singleton<T> getInstance(T value) {
        if (instance == null) {
            instance = new Singleton<>(value);
        }
        return (Singleton<T>) instance;
    }
}

// Generic Factory
public interface Product { }

public class ConcreteProduct implements Product { }

public class GenericFactory<T extends Product> {
    public T createProduct(Class<T> productClass) 
            throws InstantiationException, 
                   IllegalAccessException {
        return productClass.newInstance();
    }
}

// Generic Builder
public class GenericBuilder<T> {
    private final T instance;
    private final Map<String, Consumer<Object>> setters = new HashMap<>();

    public GenericBuilder(Class<T> clazz) 
            throws InstantiationException, 
                   IllegalAccessException {
        this.instance = clazz.newInstance();
    }

    public <U> GenericBuilder<T> with(String property, U value) {
        Consumer<Object> setter = setters.get(property);
        if (setter != null) {
            setter.accept(value);
        }
        return this;
    }

    public T build() {
        return instance;
    }
}