Skip to content

泛型

泛型是 Java 5 引入的特性,允许在定义类、接口和方法时使用类型参数。泛型提供了编译时类型安全检查,消除了大部分类型转换。

泛型概述

为什么需要泛型

java
import java.util.*;

public class WhyGenerics {
    public static void main(String[] args) {
        // 没有泛型时(Java 5 之前)
        List list = new ArrayList();
        list.add("Hello");
        list.add(123);  // 可以添加任意类型
        
        String str = (String) list.get(0);  // 需要强制类型转换
        // String str2 = (String) list.get(1);  // 运行时 ClassCastException
        
        // 使用泛型
        List<String> genericList = new ArrayList<>();
        genericList.add("Hello");
        // genericList.add(123);  // 编译错误:类型检查
        
        String genericStr = genericList.get(0);  // 不需要类型转换
    }
}

泛型的优势

text
┌─────────────────────────────────────────────────────────────────┐
│                      泛型的优势                                  │
├─────────────────────────────────────────────────────────────────┤
│  1. 类型安全:编译时检查类型,避免运行时类型转换异常              │
│  2. 消除类型转换:不需要手动进行类型转换                         │
│  3. 代码复用:可以编写通用的代码,适用于多种类型                 │
│  4. 提高可读性:代码意图更加清晰                                 │
└─────────────────────────────────────────────────────────────────┘

泛型类

定义泛型类

java
// 泛型类:类型参数 T
public class Box<T> {
    private T content;
    
    public Box() {
    }
    
    public Box(T content) {
        this.content = content;
    }
    
    public T getContent() {
        return content;
    }
    
    public void setContent(T content) {
        this.content = content;
    }
    
    public static void main(String[] args) {
        // 使用泛型类
        Box<String> stringBox = new Box<>("Hello");
        System.out.println("字符串盒子:" + stringBox.getContent());
        
        Box<Integer> integerBox = new Box<>(100);
        System.out.println("整数盒子:" + integerBox.getContent());
        
        Box<Double> doubleBox = new Box<>(3.14);
        System.out.println("浮点数盒子:" + doubleBox.getContent());
    }
}

多个类型参数

java
// 多个类型参数
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;
    }
    
    @Override
    public String toString() {
        return "(" + key + ", " + value + ")";
    }
    
    public static void main(String[] args) {
        Pair<String, Integer> nameAge = new Pair<>("张三", 25);
        System.out.println("姓名-年龄:" + nameAge);
        
        Pair<String, String> capital = new Pair<>("中国", "北京");
        System.out.println("国家-首都:" + capital);
    }
}

泛型接口

定义泛型接口

java
// 泛型接口
interface Container<T> {
    void add(T item);
    T get(int index);
    int size();
}

// 实现泛型接口:指定具体类型
class StringContainer implements Container<String> {
    private List<String> items = new ArrayList<>();
    
    @Override
    public void add(String item) {
        items.add(item);
    }
    
    @Override
    public String get(int index) {
        return items.get(index);
    }
    
    @Override
    public int size() {
        return items.size();
    }
}

// 实现泛型接口:保留泛型
class GenericContainer<T> implements Container<T> {
    private List<T> items = new ArrayList<>();
    
    @Override
    public void add(T item) {
        items.add(item);
    }
    
    @Override
    public T get(int index) {
        return items.get(index);
    }
    
    @Override
    public int size() {
        return items.size();
    }
}

public class GenericInterfaceDemo {
    public static void main(String[] args) {
        Container<String> stringContainer = new StringContainer();
        stringContainer.add("Hello");
        System.out.println(stringContainer.get(0));
        
        Container<Integer> intContainer = new GenericContainer<>();
        intContainer.add(100);
        System.out.println(intContainer.get(0));
    }
}

泛型方法

定义泛型方法

java
import java.util.Arrays;

public class GenericMethod {
    public static void main(String[] args) {
        // 调用泛型方法
        Integer[] intArray = {1, 2, 3, 4, 5};
        String[] strArray = {"A", "B", "C"};
        
        printArray(intArray);
        printArray(strArray);
        
        // 泛型方法返回值
        Integer max = findMax(intArray);
        System.out.println("最大值:" + max);
        
        // 泛型方法与类型推断
        String middle = getMiddle(strArray);
        System.out.println("中间元素:" + middle);
    }
    
    // 泛型方法:<T> 放在返回值类型之前
    public static <T> void printArray(T[] array) {
        for (T item : array) {
            System.out.print(item + " ");
        }
        System.out.println();
    }
    
    // 泛型方法带返回值
    public static <T extends Comparable<T>> T findMax(T[] array) {
        if (array == null || array.length == 0) {
            return null;
        }
        T max = array[0];
        for (int i = 1; i < array.length; i++) {
            if (array[i].compareTo(max) > 0) {
                max = array[i];
            }
        }
        return max;
    }
    
    // 泛型方法
    public static <T> T getMiddle(T[] array) {
        return array[array.length / 2];
    }
    
    // 非泛型类中的泛型方法
    public static <K, V> void printPair(K key, V value) {
        System.out.println("Key: " + key + ", Value: " + value);
    }
}

泛型方法与泛型类

java
public class GenericMethodVsClass<T> {
    private T value;
    
    // 泛型类的方法(使用类的类型参数)
    public void setValue(T value) {
        this.value = value;
    }
    
    // 泛型方法(使用自己的类型参数)
    public static <E> void print(E item) {
        System.out.println(item);
    }
    
    // 泛型方法(与类的类型参数不同)
    public <E> void process(E item) {
        System.out.println("处理:" + item);
    }
    
    public static void main(String[] args) {
        GenericMethodVsClass<String> obj = new GenericMethodVsClass<>();
        obj.setValue("Hello");  // T = String
        
        GenericMethodVsClass.print(123);  // E = Integer
        obj.process(3.14);  // E = Double
    }
}

类型通配符

无界通配符

java
import java.util.*;

public class UnboundedWildcard {
    public static void main(String[] args) {
        List<String> strings = Arrays.asList("A", "B", "C");
        List<Integer> integers = Arrays.asList(1, 2, 3);
        
        printList(strings);
        printList(integers);
    }
    
    // 无界通配符:可以接受任何类型
    public static void printList(List<?> list) {
        for (Object item : list) {
            System.out.print(item + " ");
        }
        System.out.println();
        
        // list.add("new");  // 编译错误:不能添加元素(除了 null)
        Object obj = list.get(0);  // 可以读取,类型为 Object
    }
}

上界通配符

java
import java.util.*;

public class UpperBoundedWildcard {
    public static void main(String[] args) {
        List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5);
        List<Double> doubles = Arrays.asList(1.5, 2.5, 3.5);
        List<Number> numbers = Arrays.asList(10, 20.5, 30);
        
        System.out.println("Integer 和:" + sum(integers));
        System.out.println("Double 和:" + sum(doubles));
        System.out.println("Number 和:" + sum(numbers));
    }
    
    // 上界通配符:接受 Number 或其子类
    public static double sum(List<? extends Number> list) {
        double total = 0;
        for (Number num : list) {
            total += num.doubleValue();
        }
        
        // list.add(10);  // 编译错误:不能添加元素
        // 因为不知道具体是 Integer、Double 还是其他子类
        
        return total;
    }
}

下界通配符

java
import java.util.*;

public class LowerBoundedWildcard {
    public static void main(String[] args) {
        List<Integer> integers = new ArrayList<>();
        List<Number> numbers = new ArrayList<>();
        List<Object> objects = new ArrayList<>();
        
        addNumbers(integers);  // 可以添加 Integer 及其父类
        addNumbers(numbers);
        addNumbers(objects);
        
        System.out.println("integers: " + integers);
        System.out.println("numbers: " + numbers);
        System.out.println("objects: " + objects);
    }
    
    // 下界通配符:接受 Integer 或其父类
    public static void addNumbers(List<? super Integer> list) {
        list.add(1);
        list.add(2);
        list.add(3);
        
        // Integer i = list.get(0);  // 编译错误:读取的类型不确定
        Object obj = list.get(0);  // 只能作为 Object 读取
    }
}

PECS 原则

text
┌─────────────────────────────────────────────────────────────────┐
│                       PECS 原则                                 │
├─────────────────────────────────────────────────────────────────┤
│  Producer Extends, Consumer Super                               │
│                                                                 │
│  如果需要从集合中读取数据(生产者),使用 ? extends T            │
│  如果需要向集合中写入数据(消费者),使用 ? super T              │
│  如果既要读又要写,使用精确的类型 T                              │
│                                                                 │
│  示例:                                                          │
│    public static <T> void copy(                                 │
│        List<? super T> dest,  // 消费者:写入数据               │
│        List<? extends T> src   // 生产者:读取数据              │
│    ) {                                                          │
│        for (T item : src) {                                     │
│            dest.add(item);                                      │
│        }                                                        │
│    }                                                            │
└─────────────────────────────────────────────────────────────────┘

类型擦除

泛型的实现原理

java
import java.util.*;

public class TypeErasure {
    public static void main(String[] args) {
        // 泛型类型在编译时被擦除
        List<String> strings = new ArrayList<>();
        List<Integer> integers = new ArrayList<>();
        
        // 运行时类型相同
        System.out.println(strings.getClass() == integers.getClass());  // true
        System.out.println(strings.getClass().getName());  // java.util.ArrayList
        
        // 泛型信息在运行时不可用
        // if (strings instanceof List<String>) { }  // 编译错误
        if (strings instanceof List) { }  // 正确
    }
}

类型擦除规则

text
┌─────────────────────────────────────────────────────────────────┐
│                      类型擦除规则                                │
├─────────────────────────────────────────────────────────────────┤
│  1. 无界类型参数(<T>)擦除为 Object                             │
│  2. 有界类型参数(<T extends Number>)擦除为边界类型 Number       │
│  3. 多个边界取第一个边界类型                                     │
│  4. 泛型方法中的类型参数同样被擦除                               │
│  5. 桥方法用于保持多态性                                         │
└─────────────────────────────────────────────────────────────────┘
java
// 编译前
class GenericClass<T extends Number> {
    private T value;
    
    public T getValue() {
        return value;
    }
    
    public void setValue(T value) {
        this.value = value;
    }
}

// 编译后(类型擦除)
class GenericClass {
    private Number value;
    
    public Number getValue() {
        return value;
    }
    
    public void setValue(Number value) {
        this.value = value;
    }
}

泛型限制

不能使用的场景

java
import java.util.*;

public class GenericLimitations {
    public static void main(String[] args) {
        // 1. 不能实例化类型参数
        // T item = new T();  // 编译错误
        
        // 2. 不能创建泛型数组
        // T[] array = new T[10];  // 编译错误
        // List<String>[] array = new List<String>[10];  // 编译错误
        
        // 正确的方式
        List<?>[] array = new List<?>[10];
        @SuppressWarnings("unchecked")
        List<String>[] stringLists = (List<String>[]) new List[10];
        
        // 3. 不能使用基本类型
        // List<int> intList = new ArrayList<>();  // 编译错误
        List<Integer> intList = new ArrayList<>();  // 使用包装类
        
        // 4. 不能抛出或捕获泛型类异常
        // class MyException<T> extends Exception { }  // 编译错误
        
        // 5. 不能重载具有相同擦除类型的方法
        // public void method(List<String> list) { }
        // public void method(List<Integer> list) { }  // 编译错误
    }
    
    // 使用反射创建泛型数组
    @SuppressWarnings("unchecked")
    public static <T> T[] createArray(Class<T> type, int size) {
        return (T[]) java.lang.reflect.Array.newInstance(type, size);
    }
}

泛型最佳实践

命名约定

java
// 泛型类型参数命名约定
public class NamingConventions {
    // T - Type(类型)
    public static <T> void process(T item) { }
    
    // E - Element(元素,常用于集合)
    public static <E> void addToCollection(Collection<E> collection, E element) { }
    
    // K - Key(键)
    // V - Value(值)
    public static <K, V> void putToMap(Map<K, V> map, K key, V value) { }
    
    // N - Number(数字)
    public static <N extends Number> double doubleValue(N number) {
        return number.doubleValue();
    }
    
    // R - Result(结果)
    // S, U, V 等 - 多个类型参数
    public static <S, T> Pair<S, T> createPair(S first, T second) {
        return new Pair<>(first, second);
    }
}

泛型工具方法

java
import java.util.*;

public class GenericUtils {
    
    // 交换数组元素
    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> int indexOf(T[] array, T target) {
        for (int i = 0; i < array.length; i++) {
            if (array[i].equals(target)) {
                return i;
            }
        }
        return -1;
    }
    
    // 过滤集合
    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;
    }
    
    @FunctionalInterface
    interface Predicate<T> {
        boolean test(T t);
    }
    
    @FunctionalInterface
    interface Function<T, R> {
        R apply(T t);
    }
    
    public static void main(String[] args) {
        Integer[] numbers = {1, 2, 3, 4, 5};
        swap(numbers, 0, 4);
        System.out.println("交换后:" + Arrays.toString(numbers));
        
        String[] names = {"张三", "李四", "王五"};
        System.out.println("李四的索引:" + indexOf(names, "李四"));
        
        List<Integer> nums = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        List<Integer> evens = filter(nums, n -> n % 2 == 0);
        System.out.println("偶数:" + evens);
        
        List<String> nameList = Arrays.asList("张三", "李四", "王五");
        List<Integer> lengths = map(nameList, String::length);
        System.out.println("名字长度:" + lengths);
    }
}

小结

本章我们学习了:

  • 泛型概述:类型安全、消除类型转换
  • 泛型类:定义和使用泛型类
  • 泛型接口:实现泛型接口
  • 泛型方法:定义和使用泛型方法
  • 类型通配符:无界、上界、下界通配符
  • PECS 原则:Producer Extends, Consumer Super
  • 类型擦除:泛型的实现原理
  • 泛型限制:不能使用的场景

下一章,我们将学习 注解,了解 Java 注解的使用。