Java 泛型方法

泛型方法是在方法層級定義型別參數,讓單一方法可以處理不同型別的參數。

基本語法

型別參數放在回傳型別之前:

public class Utility {
    // 泛型方法
    public static <T> void print(T item) {
        System.out.println(item);
    }

    public static <T> T getFirst(T[] array) {
        if (array == null || array.length == 0) {
            return null;
        }
        return array[0];
    }
}

// 使用
Utility.print("Hello");    // 自動推斷為 String
Utility.print(123);        // 自動推斷為 Integer

String first = Utility.getFirst(new String[]{"a", "b", "c"});  // "a"
Integer num = Utility.getFirst(new Integer[]{1, 2, 3});        // 1

型別推斷

Java 通常可以自動推斷型別參數:

// 自動推斷
List<String> list1 = Arrays.asList("a", "b", "c");

// 明確指定(偶爾需要)
List<String> list2 = Arrays.<String>asList("a", "b", "c");

// 需要明確指定的情況
Collections.<String>emptyList();

多個型別參數

public static <K, V> Map<K, V> createMap(K key, V value) {
    Map<K, V> map = new HashMap<>();
    map.put(key, value);
    return map;
}

// 使用
Map<String, Integer> map = createMap("age", 25);

有界型別參數

// T 必須實作 Comparable
public static <T extends Comparable<T>> T max(T a, T b) {
    return a.compareTo(b) > 0 ? a : b;
}

// 使用
int maxInt = max(10, 20);        // 20
String maxStr = max("a", "z");   // "z"

// 多重上界
public static <T extends Number & Comparable<T>> T max(T a, T b) {
    return a.compareTo(b) > 0 ? a : b;
}

泛型方法 vs 泛型類別

// 泛型類別:型別在類別層級定義
public class Box<T> {
    private T content;

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

    public T get() {
        return content;
    }
}

// 泛型方法:型別在方法層級定義
public class Utility {
    public static <T> T getFirst(List<T> list) {
        return list.isEmpty() ? null : list.get(0);
    }
}

組合使用

public class Container<T> {
    private List<T> items = new ArrayList<>();

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

    // 類別的型別參數
    public T getFirst() {
        return items.isEmpty() ? null : items.get(0);
    }

    // 方法自己的型別參數
    public <U> U transform(Function<T, U> transformer) {
        if (items.isEmpty()) return null;
        return transformer.apply(items.get(0));
    }
}

// 使用
Container<String> container = new Container<>();
container.add("hello");
Integer length = container.transform(String::length);  // 5

靜態泛型方法

public class Collections {
    // 靜態泛型方法
    public static <T> List<T> singletonList(T item) {
        List<T> list = new ArrayList<>();
        list.add(item);
        return list;
    }

    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 extends Comparable<T>> void sort(List<T> list) {
        // 排序邏輯
    }
}

實例泛型方法

public class Converter {
    public <T, R> R convert(T input, Function<T, R> converter) {
        return converter.apply(input);
    }

    public <T> Optional<T> nullSafe(T value) {
        return Optional.ofNullable(value);
    }
}

// 使用
Converter converter = new Converter();
Integer length = converter.convert("hello", String::length);
Optional<String> opt = converter.nullSafe(null);

實際範例

交換陣列元素

public static <T> void swap(T[] array, int i, int j) {
    if (i < 0 || i >= array.length || j < 0 || j >= array.length) {
        throw new IndexOutOfBoundsException();
    }
    T temp = array[i];
    array[i] = array[j];
    array[j] = temp;
}

// 使用
String[] strings = {"a", "b", "c"};
swap(strings, 0, 2);  // ["c", "b", "a"]

Integer[] numbers = {1, 2, 3};
swap(numbers, 0, 1);  // [2, 1, 3]

找出最大值

public static <T extends Comparable<T>> T findMax(List<T> list) {
    if (list.isEmpty()) {
        throw new IllegalArgumentException("List is empty");
    }

    T max = list.get(0);
    for (int i = 1; i < list.size(); i++) {
        if (list.get(i).compareTo(max) > 0) {
            max = list.get(i);
        }
    }
    return max;
}

// 使用
List<Integer> numbers = Arrays.asList(3, 1, 4, 1, 5, 9);
Integer max = findMax(numbers);  // 9

List<String> words = Arrays.asList("apple", "banana", "cherry");
String maxWord = findMax(words);  // "cherry"

安全轉型

public static <T> Optional<T> safeCast(Object obj, Class<T> clazz) {
    if (clazz.isInstance(obj)) {
        return Optional.of(clazz.cast(obj));
    }
    return Optional.empty();
}

// 使用
Object value = "Hello";
Optional<String> str = safeCast(value, String.class);  // Optional["Hello"]
Optional<Integer> num = safeCast(value, Integer.class);  // Optional.empty

複製集合

public static <T> List<T> copy(Collection<? extends T> source) {
    return new ArrayList<>(source);
}

public static <T> void copyTo(Collection<? extends T> source, 
                              Collection<? super T> target) {
    target.addAll(source);
}

// 使用
List<Integer> original = Arrays.asList(1, 2, 3);
List<Integer> copied = copy(original);

List<Number> numbers = new ArrayList<>();
copyTo(original, numbers);  // Integer 是 Number 的子類別

建立物件

public static <T> T createInstance(Class<T> clazz) 
        throws ReflectiveOperationException {
    return clazz.getDeclaredConstructor().newInstance();
}

// 使用
String str = createInstance(String.class);
ArrayList list = createInstance(ArrayList.class);

過濾集合

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;
}

// 使用
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> evens = filter(numbers, n -> n % 2 == 0);  // [2, 4, 6]

List<String> words = Arrays.asList("a", "bb", "ccc", "dddd");
List<String> longWords = filter(words, s -> s.length() > 2);  // ["ccc", "dddd"]

映射集合

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;
}

// 使用
List<String> words = Arrays.asList("a", "bb", "ccc");
List<Integer> lengths = map(words, String::length);  // [1, 2, 3]
List<String> upper = map(words, String::toUpperCase);  // ["A", "BB", "CCC"]

常見錯誤

型別擦除問題

// ✗ 編譯錯誤:型別擦除後方法簽名相同
public void process(List<String> list) { }
public void process(List<Integer> list) { }  // 衝突!

// ✓ 解決方式:改用不同的方法名稱
public void processStrings(List<String> list) { }
public void processIntegers(List<Integer> list) { }

無法建立泛型陣列

// ✗ 無法建立泛型陣列
public static <T> T[] toArray(List<T> list) {
    return new T[list.size()];  // 編譯錯誤
}

// ✓ 使用 Array.newInstance
@SuppressWarnings("unchecked")
public static <T> T[] toArray(List<T> list, Class<T> clazz) {
    T[] array = (T[]) Array.newInstance(clazz, list.size());
    return list.toArray(array);
}

重點整理

  • 泛型方法在回傳型別前宣告型別參數:<T>
  • Java 通常可以自動推斷型別
  • 使用 extends 限制型別上界
  • 泛型方法可以是靜態或實例方法
  • 方法的型別參數與類別的型別參數獨立
  • 注意型別擦除帶來的限制