Skip to main content

概述

泛型(Generics)是 JDK 5 引入的特性,允许在定义类、接口和方法时使用类型参数。泛型提供了编译时类型检查,避免了强制类型转换,提高了代码的安全性和可读性。

为什么需要泛型

// 没有泛型时
List list = new ArrayList();
list.add("Hello");
list.add(123);  // 可以添加任何类型
String str = (String) list.get(0);  // 需要强制转换
String num = (String) list.get(1);  // 运行时 ClassCastException

// 使用泛型后
List<String> list = new ArrayList<>();
list.add("Hello");
// list.add(123);  // 编译错误!
String str = list.get(0);  // 无需强制转换

泛型的好处

  1. 类型安全:编译时检查类型,避免运行时错误
  2. 消除强转:无需手动进行类型转换
  3. 代码复用:同一套代码可以处理不同类型

泛型类

// 定义泛型类
public class Box<T> {
    private T content;
    
    public void set(T content) {
        this.content = content;
    }
    
    public T get() {
        return content;
    }
}

// 使用泛型类
Box<String> stringBox = new Box<>();
stringBox.set("Hello");
String str = stringBox.get();

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

多个类型参数

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

// 使用
Pair<String, Integer> pair = new Pair<>("age", 25);
String key = pair.getKey();
Integer value = pair.getValue();

泛型接口

// 定义泛型接口
public interface Generator<T> {
    T generate();
}

// 实现时指定类型
public class StringGenerator implements Generator<String> {
    @Override
    public String generate() {
        return "Hello";
    }
}

// 实现时保留泛型
public class GenericGenerator<T> implements Generator<T> {
    private T value;
    
    public GenericGenerator(T value) {
        this.value = value;
    }
    
    @Override
    public T generate() {
        return value;
    }
}

泛型方法

泛型方法可以在普通类中定义,类型参数声明在返回类型之前。
public class Utils {
    // 泛型方法
    public static <T> T getFirst(List<T> list) {
        if (list == null || list.isEmpty()) {
            return null;
        }
        return list.get(0);
    }
    
    // 多个类型参数
    public static <K, V> Map<K, V> createMap(K key, V value) {
        Map<K, V> map = new HashMap<>();
        map.put(key, value);
        return map;
    }
    
    // 泛型方法与泛型类的类型参数无关
    public static <E> void printArray(E[] array) {
        for (E element : array) {
            System.out.println(element);
        }
    }
}

// 使用
String first = Utils.getFirst(Arrays.asList("a", "b", "c"));
Map<String, Integer> map = Utils.createMap("key", 100);
Utils.printArray(new Integer[]{1, 2, 3});

类型参数命名约定

参数含义
EElement(元素)
TType(类型)
KKey(键)
VValue(值)
NNumber(数字)
S,U,V第2、3、4个类型参数

泛型边界

上界(extends)

限制类型参数必须是某个类型或其子类型。
// T 必须是 Number 或其子类
public class NumberBox<T extends Number> {
    private T number;
    
    public double doubleValue() {
        return number.doubleValue();  // 可以调用 Number 的方法
    }
}

NumberBox<Integer> intBox = new NumberBox<>();  // OK
NumberBox<Double> doubleBox = new NumberBox<>();  // OK
// NumberBox<String> strBox = new NumberBox<>();  // 编译错误!

多重边界

// T 必须同时是 A 的子类并实现 B、C 接口
// 类必须放在第一位
public class Demo<T extends Comparable<T> & Serializable> {
    // ...
}

方法中的边界

// 限制只能接受 Number 及其子类的 List
public static <T extends Number> double sum(List<T> list) {
    double total = 0;
    for (T num : list) {
        total += num.doubleValue();
    }
    return total;
}

通配符

通配符 ? 表示未知类型,主要用于方法参数。

无界通配符

// 接受任意类型的 List
public static void printList(List<?> list) {
    for (Object item : list) {
        System.out.println(item);
    }
}

printList(Arrays.asList(1, 2, 3));
printList(Arrays.asList("a", "b", "c"));

上界通配符(extends)

// 接受 Number 或其子类的 List
public static double sumList(List<? extends Number> list) {
    double total = 0;
    for (Number num : list) {
        total += num.doubleValue();
    }
    return total;
}

sumList(Arrays.asList(1, 2, 3));           // List<Integer>
sumList(Arrays.asList(1.0, 2.0, 3.0));     // List<Double>

下界通配符(super)

// 接受 Integer 或其父类的 List
public static void addNumbers(List<? super Integer> list) {
    list.add(1);
    list.add(2);
    list.add(3);
}

addNumbers(new ArrayList<Integer>());  // OK
addNumbers(new ArrayList<Number>());   // OK
addNumbers(new ArrayList<Object>());   // OK
// addNumbers(new ArrayList<Double>()); // 编译错误!

PECS 原则

Producer Extends, Consumer Super
  • 如果需要读取数据,使用 extends(生产者)
  • 如果需要写入数据,使用 super(消费者)
  • 如果既读又写,不使用通配符
// 从 source 读取,写入 dest
public static <T> void copy(List<? extends T> source, List<? super T> dest) {
    for (T item : source) {
        dest.add(item);
    }
}

通配符与泛型的区别

特性泛型类型参数 T通配符 ?
位置类、接口、方法的定义主要用于方法参数
是否可引用可以在代码中引用不能引用
赋值可以赋值只能读取,不能添加
多次使用可以多次使用表示同一类型每个 ? 表示不同类型

类型擦除

Java 的泛型是通过类型擦除实现的,在编译后泛型信息会被擦除。
// 编译前
public class Box<T> {
    private T content;
    public void set(T content) { this.content = content; }
    public T get() { return content; }
}

// 编译后(类型擦除)
public class Box {
    private Object content;
    public void set(Object content) { this.content = content; }
    public Object get() { return content; }
}

类型擦除的影响

// 1. 无法使用基本类型
// List<int> list;  // 错误!必须使用 Integer

// 2. 运行时无法获取泛型类型
List<String> strings = new ArrayList<>();
List<Integer> integers = new ArrayList<>();
System.out.println(strings.getClass() == integers.getClass());  // true

// 3. 无法创建泛型数组
// T[] array = new T[10];  // 编译错误

// 4. 无法 instanceof 泛型类型
// if (obj instanceof List<String>) {}  // 编译错误

// 5. 无法创建泛型类型的实例
// T instance = new T();  // 编译错误

获取泛型类型信息

虽然运行时泛型信息被擦除,但可以通过反射获取类声明的泛型类型:
public class UserDao extends BaseDao<User> { }

// 获取父类的泛型参数
Class<UserDao> clazz = UserDao.class;
Type genericSuperclass = clazz.getGenericSuperclass();
ParameterizedType pt = (ParameterizedType) genericSuperclass;
Type[] types = pt.getActualTypeArguments();
Class<?> entityClass = (Class<?>) types[0];
System.out.println(entityClass);  // class User

泛型的限制

不能使用基本类型

// List<int> list;  // 错误
List<Integer> list = new ArrayList<>();  // 正确

不能创建泛型数组

// 直接创建泛型数组是不允许的
// T[] array = new T[10];  // 编译错误
// List<String>[] array = new List<String>[10];  // 编译错误

// 替代方案
List<String>[] array = (List<String>[]) new List<?>[10];
Object[] objArray = new Object[10];

不能实例化类型参数

public class Factory<T> {
    // T instance = new T();  // 编译错误
    
    // 替代方案:通过 Class 创建
    public T create(Class<T> clazz) throws Exception {
        return clazz.getDeclaredConstructor().newInstance();
    }
}

静态成员不能使用类的类型参数

public class Box<T> {
    // private static T value;  // 编译错误
    // public static T getValue() { }  // 编译错误
    
    // 但静态方法可以有自己的泛型参数
    public static <E> E getValue(E value) {
        return value;
    }
}

泛型实战

泛型工具类

public class CollectionUtils {
    // 判空
    public static <T> boolean isEmpty(Collection<T> collection) {
        return collection == null || collection.isEmpty();
    }
    
    // 获取第一个元素
    public static <T> T getFirst(List<T> list) {
        return isEmpty(list) ? null : list.get(0);
    }
    
    // 过滤
    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;
    }
}

泛型响应类

public class Result<T> {
    private int code;
    private String message;
    private T data;
    
    private Result(int code, String message, T data) {
        this.code = code;
        this.message = message;
        this.data = data;
    }
    
    public static <T> Result<T> success(T data) {
        return new Result<>(200, "success", data);
    }
    
    public static <T> Result<T> error(int code, String message) {
        return new Result<>(code, message, null);
    }
    
    // Getter 方法...
}

// 使用
Result<User> result = Result.success(new User("张三"));
Result<List<User>> listResult = Result.success(Arrays.asList(user1, user2));

泛型 DAO

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

public class UserDaoImpl implements BaseDao<User, Long> {
    @Override
    public User findById(Long id) { /* ... */ }
    
    @Override
    public List<User> findAll() { /* ... */ }
    
    @Override
    public void save(User entity) { /* ... */ }
    
    @Override
    public void update(User entity) { /* ... */ }
    
    @Override
    public void deleteById(Long id) { /* ... */ }
}