泛型机制详解
一、知识概述
泛型(Generics)是Java 5引入的重要特性,它允许在定义类、接口和方法时使用类型参数。泛型的核心目的是提供编译时类型安全检测,消除代码中的强制类型转换,提高代码的可读性和安全性。
为什么需要泛型?
在没有泛型之前,Java集合只能存储Object类型,这导致:
- 类型不安全 :可以往List中添加任意类型,运行时可能抛出
ClassCastException - 需要强制转换:每次取出元素都需要类型转换
- 错误在运行时才发现:编译器无法帮助检查类型错误
java
// 没有泛型的时代
List list = new ArrayList();
list.add("Hello");
list.add(123); // 编译通过,运行时出错
String s = (String) list.get(0); // 需要强制转换
有了泛型后:
java
// 使用泛型
List<String> list = new ArrayList<>();
list.add("Hello");
// list.add(123); // 编译错误,提前发现问题
String s = list.get(0); // 无需转换
二、知识点详细讲解
2.1 泛型类
泛型类是在类定义时声明类型参数,该类型参数可以在类内部使用。
java
/**
* 泛型类示例:通用容器类
* @param <T> 容器中元素的类型
*/
public class Container<T> {
private T value;
public void set(T value) {
this.value = value;
}
public T get() {
return value;
}
// 静态方法不能使用类的类型参数T
// public static void staticMethod(T t) {} // 编译错误
// 静态方法可以定义自己的类型参数
public static <E> void staticMethod(E e) {
System.out.println(e);
}
}
使用示例:
java
Container<String> stringContainer = new Container<>();
stringContainer.set("Hello");
String value = stringContainer.get();
Container<Integer> intContainer = new Container<>();
intContainer.set(100);
Integer num = intContainer.get();
2.2 泛型接口
泛型接口定义方式与泛型类类似。
java
/**
* 泛型接口:生成器接口
* @param <T> 生成的对象类型
*/
public interface Generator<T> {
T generate();
}
// 实现方式1:指定具体类型
public class StringGenerator implements Generator<String> {
@Override
public String generate() {
return "Generated String";
}
}
// 实现方式2:保留类型参数
public class GenericGenerator<T> implements Generator<T> {
private T value;
public GenericGenerator(T value) {
this.value = value;
}
@Override
public T generate() {
return value;
}
}
2.3 泛型方法
泛型方法是在方法声明时定义类型参数,可以在普通类或泛型类中定义。
java
public class GenericMethodDemo {
/**
* 泛型方法:交换数组中两个元素的位置
* @param <T> 数组元素类型
* @param array 数组
* @param i 第一个索引
* @param j 第二个索引
*/
public static <T> void swap(T[] array, int i, int j) {
T temp = array[i];
array[i] = array[j];
array[j] = temp;
}
/**
* 泛型方法:打印任意类型的数组
*/
public static <E> void printArray(E[] array) {
for (E element : array) {
System.out.print(element + " ");
}
System.out.println();
}
/**
* 泛型方法与可变参数
*/
@SafeVarargs
public static <T> List<T> createList(T... elements) {
List<T> list = new ArrayList<>();
Collections.addAll(list, elements);
return list;
}
}
调用示例:
java
Integer[] nums = {1, 2, 3, 4, 5};
GenericMethodDemo.swap(nums, 0, 4);
GenericMethodDemo.printArray(nums); // 5 2 3 4 1
List<String> names = GenericMethodDemo.<String>createList("A", "B", "C");
// 类型推断,可以省略<String>
List<String> names2 = GenericMethodDemo.createList("A", "B", "C");
2.4 类型参数的限定
有时需要对类型参数进行约束,比如要求类型必须实现某个接口。
java
/**
* 类型参数限定:T必须实现Comparable接口
* <T extends Comparable<T>> 表示T必须是可以与自己比较的类型
*/
public static <T extends Comparable<T>> T max(T a, T b) {
return a.compareTo(b) >= 0 ? a : b;
}
/**
* 多重限定:T必须同时满足多个条件
* 类在前,接口在后
*/
public static <T extends Number & Comparable<T>> T min(T a, T b) {
return a.compareTo(b) <= 0 ? a : b;
}
2.5 通配符
通配符用于处理泛型的类型不确定情况。
2.5.1 无界通配符 ?
java
/**
* 无界通配符:可以接受任何类型
*/
public static void printList(List<?> list) {
for (Object elem : list) {
System.out.print(elem + " ");
}
System.out.println();
// list.add("test"); // 编译错误,不能添加元素(除了null)
Object obj = list.get(0); // 可以读取,但类型是Object
}
2.5.2 上界通配符 ? extends T
java
/**
* 上界通配符:接受T及其子类
* 适合读取场景(生产者)
*/
public static double sum(List<? extends Number> list) {
double total = 0;
for (Number num : list) {
total += num.doubleValue();
}
// list.add(10); // 编译错误,不能写入
return total;
}
// 使用示例
List<Integer> integers = Arrays.asList(1, 2, 3);
List<Double> doubles = Arrays.asList(1.1, 2.2, 3.3);
System.out.println(sum(integers)); // 6.0
System.out.println(sum(doubles)); // 6.6
2.5.3 下界通配符 ? super T
java
/**
* 下界通配符:接受T及其父类
* 适合写入场景(消费者)
*/
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接收
}
// 使用示例
List<Number> numbers = new ArrayList<>();
addNumbers(numbers);
System.out.println(numbers); // [1, 2, 3]
List<Object> objects = new ArrayList<>();
addNumbers(objects);
System.out.println(objects); // [1, 2, 3]
2.6 PECS原则
PECS = Producer Extends, Consumer Super
- Producer(生产者) :如果只需要从集合中读取(生产),使用
? extends T - Consumer(消费者) :如果只需要向集合中写入(消费),使用
? super T
java
/**
* 复制方法:从src读取(生产者),写入dest(消费者)
*/
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
for (int i = 0; i < src.size(); i++) {
dest.set(i, src.get(i));
}
}
// 使用示例
List<Integer> integers = Arrays.asList(1, 2, 3);
List<Number> numbers = new ArrayList<>(Arrays.asList(0, 0, 0));
copy(numbers, integers); // Integer -> Number(子类到父类)
System.out.println(numbers); // [1, 2, 3]
2.7 类型擦除
Java泛型是通过**类型擦除(Type Erasure)**实现的,编译后会擦除类型参数信息。
java
// 编译前
public class Box<T> {
private T value;
public void set(T value) { this.value = value; }
public T get() { return value; }
}
// 编译后(类型擦除)
public class Box {
private Object value;
public void set(Object value) { this.value = value; }
public Object get() { return value; }
}
类型擦除的影响:
- 泛型类型不能是基本类型(需要使用包装类)
- 不能创建泛型数组
- 不能实例化类型参数
- 静态上下文中不能使用类的类型参数
java
// 编译错误示例
// List<int> intList = new ArrayList<>(); // 不能使用基本类型
// T[] array = new T[10]; // 不能创建泛型数组
// T obj = new T(); // 不能实例化类型参数
三、可运行Java代码示例
完整示例:泛型工具类
java
import java.util.*;
import java.util.stream.Collectors;
/**
* 泛型工具类示例
*/
public class GenericUtils {
/**
* 查找列表中满足条件的元素
*/
public static <T> List<T> filter(List<T> list, Predicate<T> predicate) {
return list.stream()
.filter(predicate)
.collect(Collectors.toList());
}
/**
* 将列表转换映射
*/
public static <T, R> List<R> map(List<T> list, Function<T, R> function) {
return list.stream()
.map(function)
.collect(Collectors.toList());
}
/**
* 查找第一个满足条件的元素
*/
public static <T> Optional<T> findFirst(List<T> list, Predicate<T> predicate) {
return list.stream()
.filter(predicate)
.findFirst();
}
/**
* 分组
*/
public static <T, K> Map<K, List<T>> groupBy(List<T> list, Function<T, K> classifier) {
return list.stream()
.collect(Collectors.groupingBy(classifier));
}
/**
* 判断列表是否包含满足条件的元素
*/
public static <T> boolean anyMatch(List<T> list, Predicate<T> predicate) {
return list.stream().anyMatch(predicate);
}
// 简化的函数式接口(Java 8已有java.util.function包)
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 过滤偶数
List<Integer> evens = filter(numbers, n -> n % 2 == 0);
System.out.println("偶数: " + evens);
// 平方
List<Integer> squares = map(numbers, n -> n * n);
System.out.println("平方: " + squares);
// 查找第一个大于5的数
Optional<Integer> first = findFirst(numbers, n -> n > 5);
System.out.println("第一个大于5的数: " + first.orElse(null));
// 分组:奇偶
Map<Boolean, List<Integer>> grouped = groupBy(numbers, n -> n % 2 == 0);
System.out.println("偶数组: " + grouped.get(true));
System.out.println("奇数组: " + grouped.get(false));
}
}
完整示例:泛型缓存容器
java
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* 泛型缓存容器
* @param <K> 键类型
* @param <V> 值类型
*/
public class GenericCache<K, V> {
private final Map<K, CacheEntry<V>> cache = new HashMap<>();
private final long defaultExpireMillis;
/**
* 缓存条目
*/
private static class CacheEntry<V> {
private final V value;
private final long expireTime;
CacheEntry(V value, long expireMillis) {
this.value = value;
this.expireTime = System.currentTimeMillis() + expireMillis;
}
boolean isExpired() {
return System.currentTimeMillis() > expireTime;
}
}
public GenericCache() {
this(TimeUnit.MINUTES.toMillis(30)); // 默认30分钟过期
}
public GenericCache(long defaultExpireMillis) {
this.defaultExpireMillis = defaultExpireMillis;
}
/**
* 存入缓存
*/
public void put(K key, V value) {
put(key, value, defaultExpireMillis);
}
/**
* 存入缓存,指定过期时间
*/
public void put(K key, V value, long expireMillis) {
cache.put(key, new CacheEntry<>(value, expireMillis));
}
/**
* 获取缓存
*/
public V get(K key) {
CacheEntry<V> entry = cache.get(key);
if (entry == null || entry.isExpired()) {
cache.remove(key);
return null;
}
return entry.value;
}
/**
* 获取缓存,如果不存在则通过loader加载
*/
public V getOrDefault(K key, CacheLoader<K, V> loader) {
V value = get(key);
if (value == null) {
value = loader.load(key);
if (value != null) {
put(key, value);
}
}
return value;
}
/**
* 删除缓存
*/
public void remove(K key) {
cache.remove(key);
}
/**
* 清空缓存
*/
public void clear() {
cache.clear();
}
/**
* 缓存大小
*/
public int size() {
return cache.size();
}
/**
* 清理过期条目
*/
public void cleanExpired() {
cache.entrySet().removeIf(entry -> entry.getValue().isExpired());
}
/**
* 缓存加载器接口
*/
@FunctionalInterface
public interface CacheLoader<K, V> {
V load(K key);
}
public static void main(String[] args) throws InterruptedException {
GenericCache<String, String> cache = new GenericCache<>(1000); // 1秒过期
cache.put("key1", "value1");
System.out.println("立即获取: " + cache.get("key1")); // value1
Thread.sleep(1500);
System.out.println("1.5秒后获取: " + cache.get("key1")); // null
// 使用loader
String value = cache.getOrDefault("key2", k -> "loaded_" + k);
System.out.println("Loader加载: " + value); // loaded_key2
}
}
四、实战应用场景
场景1:通用DAO层设计
java
/**
* 通用DAO接口
* @param <T> 实体类型
* @param <ID> 主键类型
*/
public interface GenericDao<T, ID> {
T findById(ID id);
List<T> findAll();
void save(T entity);
void update(T entity);
void delete(ID id);
}
/**
* 通用DAO抽象实现
*/
public abstract class AbstractGenericDao<T, ID> implements GenericDao<T, ID> {
protected abstract Class<T> getEntityClass();
// 模拟数据库操作
protected Map<ID, T> dataStore = new HashMap<>();
@Override
public T findById(ID id) {
return dataStore.get(id);
}
@Override
public List<T> findAll() {
return new ArrayList<>(dataStore.values());
}
@Override
public void save(T entity) {
ID id = extractId(entity);
dataStore.put(id, entity);
}
@Override
public void update(T entity) {
save(entity);
}
@Override
public void delete(ID id) {
dataStore.remove(id);
}
protected abstract ID extractId(T entity);
}
/**
* 用户实体
*/
class User {
private Long id;
private String name;
private String email;
public User(Long id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}
public Long getId() { return id; }
public String getName() { return name; }
public String getEmail() { return email; }
@Override
public String toString() {
return "User{id=" + id + ", name='" + name + "', email='" + email + "'}";
}
}
/**
* 用户DAO
*/
class UserDao extends AbstractGenericDao<User, Long> {
@Override
protected Class<User> getEntityClass() {
return User.class;
}
@Override
protected Long extractId(User entity) {
return entity.getId();
}
}
// 使用示例
class DaoDemo {
public static void main(String[] args) {
UserDao userDao = new UserDao();
userDao.save(new User(1L, "张三", "zhangsan@example.com"));
userDao.save(new User(2L, "李四", "lisi@example.com"));
User user = userDao.findById(1L);
System.out.println("查找用户: " + user);
System.out.println("所有用户: " + userDao.findAll());
}
}
场景2:类型安全的构建器
java
/**
* 泛型构建器:支持链式调用和类型安全
*/
public class QueryBuilder<T> {
private Class<T> entityClass;
private List<String> conditions = new ArrayList<>();
private String orderBy;
private Integer limit;
private Integer offset;
private QueryBuilder(Class<T> entityClass) {
this.entityClass = entityClass;
}
public static <T> QueryBuilder<T> from(Class<T> entityClass) {
return new QueryBuilder<>(entityClass);
}
public QueryBuilder<T> where(String condition) {
conditions.add(condition);
return this;
}
public QueryBuilder<T> andWhere(String condition) {
conditions.add("AND " + condition);
return this;
}
public QueryBuilder<T> orWhere(String condition) {
conditions.add("OR " + condition);
return this;
}
public QueryBuilder<T> orderBy(String field, boolean asc) {
this.orderBy = "ORDER BY " + field + (asc ? " ASC" : " DESC");
return this;
}
public QueryBuilder<T> limit(int limit) {
this.limit = limit;
return this;
}
public QueryBuilder<T> offset(int offset) {
this.offset = offset;
return this;
}
public String buildSQL() {
StringBuilder sql = new StringBuilder("SELECT * FROM ");
sql.append(entityClass.getSimpleName().toLowerCase());
if (!conditions.isEmpty()) {
sql.append(" WHERE ");
sql.append(String.join(" ", conditions));
}
if (orderBy != null) {
sql.append(" ").append(orderBy);
}
if (limit != null) {
sql.append(" LIMIT ").append(limit);
}
if (offset != null) {
sql.append(" OFFSET ").append(offset);
}
return sql.toString();
}
// 模拟执行查询
public List<T> execute() {
String sql = buildSQL();
System.out.println("执行SQL: " + sql);
System.out.println("返回类型: " + entityClass.getSimpleName());
return Collections.emptyList();
}
}
// 使用示例
class QueryBuilderDemo {
public static void main(String[] args) {
List<User> users = QueryBuilder.from(User.class)
.where("status = 'active'")
.andWhere("age > 18")
.orderBy("created_at", false)
.limit(10)
.offset(0)
.execute();
}
}
场景3:事件总线
java
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
/**
* 泛型事件总线
*/
public class EventBus {
private final Map<Class<?>, List<Consumer<?>>> subscribers = new ConcurrentHashMap<>();
/**
* 订阅事件
*/
public <T> void subscribe(Class<T> eventType, Consumer<T> handler) {
subscribers.computeIfAbsent(eventType, k -> new ArrayList<>()).add(handler);
}
/**
* 发布事件
*/
@SuppressWarnings("unchecked")
public <T> void publish(T event) {
List<Consumer<?>> handlers = subscribers.get(event.getClass());
if (handlers != null) {
for (Consumer<?> handler : handlers) {
((Consumer<T>) handler).accept(event);
}
}
}
/**
* 取消订阅
*/
public <T> void unsubscribe(Class<T> eventType, Consumer<T> handler) {
List<Consumer<?>> handlers = subscribers.get(eventType);
if (handlers != null) {
handlers.remove(handler);
}
}
}
/**
* 事件类
*/
class UserCreatedEvent {
private final Long userId;
private final String username;
private final Date createdAt;
public UserCreatedEvent(Long userId, String username) {
this.userId = userId;
this.username = username;
this.createdAt = new Date();
}
public Long getUserId() { return userId; }
public String getUsername() { return username; }
public Date getCreatedAt() { return createdAt; }
}
class OrderCreatedEvent {
private final String orderId;
private final double amount;
public OrderCreatedEvent(String orderId, double amount) {
this.orderId = orderId;
this.amount = amount;
}
public String getOrderId() { return orderId; }
public double getAmount() { return amount; }
}
// 使用示例
class EventBusDemo {
public static void main(String[] args) {
EventBus eventBus = new EventBus();
// 订阅用户创建事件
eventBus.subscribe(UserCreatedEvent.class, event -> {
System.out.println("处理用户创建: " + event.getUsername());
});
// 订阅订单创建事件
eventBus.subscribe(OrderCreatedEvent.class, event -> {
System.out.println("处理订单创建: " + event.getOrderId() + ", 金额: " + event.getAmount());
});
// 发布事件
eventBus.publish(new UserCreatedEvent(1L, "张三"));
eventBus.publish(new OrderCreatedEvent("ORD-001", 299.99));
}
}
五、总结与最佳实践
核心要点回顾
| 特性 | 说明 |
|---|---|
| 泛型类 | 在类声明时定义类型参数 |
| 泛型接口 | 接口可以定义类型参数 |
| 泛型方法 | 方法级别定义类型参数 |
| 类型限定 | <T extends SomeClass> 约束类型范围 |
| 通配符 | ?、? extends T、? super T 处理类型不确定情况 |
| 类型擦除 | 编译时擦除泛型信息,运行时不可用 |
最佳实践
-
优先使用泛型而非原始类型
java// 好 List<String> list = new ArrayList<>(); // 避免 List list = new ArrayList(); -
消除 unchecked 警告
java// 如果确定类型安全,使用注解抑制警告 @SuppressWarnings("unchecked") public static <T> T cast(Object obj) { return (T) obj; } -
遵循 PECS 原则
- 从集合读取用
? extends T - 向集合写入用
? super T
- 从集合读取用
-
泛型方法优先于通配符
java// 好 - 更灵活 public static <T> void swap(List<T> list, int i, int j); // 较差 - 限制了使用 public static void swap(List<?> list, int i, int j); -
利用类型推断简化代码
java// Java 7+ Map<String, List<Integer>> map = new HashMap<>(); // Java 9+ List<String> list = List.of("a", "b", "c"); -
注意类型擦除的限制
- 不能使用基本类型作为类型参数
- 不能创建泛型数组
- 不能实例化类型参数
-
合理使用泛型工具类
- Java Collections 工具类
- Java 8 Stream API
- 第三方库如 Guava
常见陷阱
java
// 陷阱1:泛型数组创建
// List<String>[] array = new List<String>[10]; // 编译错误
List<String>[] array = (List<String>[]) new List[10]; // 擦除后可行,但不安全
// 陷阱2:类型检查
List<String> strings = new ArrayList<>();
List<Integer> integers = new ArrayList<>();
// strings.getClass() == integers.getClass() // true,类型擦除
// 陷阱3:重载问题
// public void method(List<String> list) {}
// public void method(List<Integer> list) {} // 编译错误,擦除后签名相同
扩展阅读
- Java 泛型 FAQ:深入理解类型擦除、桥方法等高级概念
- Effective Java:泛型章节(第5章)提供了大量最佳实践
- Java 语言规范:了解泛型的形式化定义