[Java Backend Zero to Hello] BÀI 2.1: GENERIC
📚 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 Tkhi chỉ đọc (lấy ra) - Consumer Super: Dùng
? super Tkhi 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
instanceofvớ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