目录
- [🟡 23 泛型------类型安全的参数化编程](#🟡 23 泛型——类型安全的参数化编程)
-
- 一、为什么需要泛型
-
- [1.1 没有泛型的时代](#1.1 没有泛型的时代)
- [1.2 泛型的好处](#1.2 泛型的好处)
- 二、泛型类
-
- [2.1 基本泛型类](#2.1 基本泛型类)
- [2.2 多类型参数](#2.2 多类型参数)
- [2.3 实际应用:通用响应类](#2.3 实际应用:通用响应类)
- [2.4 泛型类的继承](#2.4 泛型类的继承)
- 三、泛型接口
-
- [3.1 基本泛型接口](#3.1 基本泛型接口)
- [3.2 实际应用:DAO模式](#3.2 实际应用:DAO模式)
- 四、泛型方法
-
- [4.1 基本泛型方法](#4.1 基本泛型方法)
- [4.2 静态泛型方法](#4.2 静态泛型方法)
- 五、类型通配符
-
- [5.1 无界通配符 `<?>`](#5.1 无界通配符
<?>) - [5.2 上界通配符 `<? extends T>`](#5.2 上界通配符
<? extends T>) - [5.3 下界通配符 `<? super T>`](#5.3 下界通配符
<? super T>) - [5.4 PECS原则(Producer Extends, Consumer Super)](#5.4 PECS原则(Producer Extends, Consumer Super))
- [5.5 通配符对比表](#5.5 通配符对比表)
- [5.1 无界通配符 `<?>`](#5.1 无界通配符
- 六、泛型约束
-
- [6.1 有界类型参数](#6.1 有界类型参数)
- [6.2 常见约束示例](#6.2 常见约束示例)
- 七、类型擦除
-
- [7.1 什么是类型擦除?](#7.1 什么是类型擦除?)
- [7.2 类型擦除的规则](#7.2 类型擦除的规则)
- [7.3 类型擦除的限制](#7.3 类型擦除的限制)
- [7.4 桥接方法](#7.4 桥接方法)
- 八、泛型的高级用法
-
- [8.1 递归类型约束](#8.1 递归类型约束)
- [8.2 泛型与反射](#8.2 泛型与反射)
- 九、常见面试题解析
-
- 面试题1:泛型的类型擦除是什么?
- 面试题2:`List<String>`和`List<Integer>`在运行时有区别吗?
- [面试题3:`<? extends T>`和`<? super T>`的区别?](#面试题3:
<? extends T>和<? super T>的区别?) - 面试题4:为什么不能创建泛型数组?
- 十、总结与下篇预告
-
- 本篇核心要点
- [🤔 互动问题](#🤔 互动问题)
- [📖 下篇预告](#📖 下篇预告)
- 参考资料
🟡 23 泛型------类型安全的参数化编程
更新日期 :2026年5月 | Java入门到精通系列 · 第三阶段·核心进阶
© 版权声明:本文为原创技术文章,转载请联系作者并注明出处。
一、为什么需要泛型
1.1 没有泛型的时代
java
// Java 5之前:没有泛型
import java.util.ArrayList;
import java.util.List;
public class WithoutGenerics {
public static void main(String[] args) {
List list = new ArrayList(); // 可以放任何类型
list.add("Hello");
list.add(123);
list.add(new Object());
// 取出时需要强制类型转换
String s = (String) list.get(0); // OK
String s2 = (String) list.get(1); // ClassCastException!
// 编译器无法检查类型安全
}
}
1.2 泛型的好处
java
// Java 5+:使用泛型
public class WithGenerics {
public static void main(String[] args) {
List<String> list = new ArrayList<>(); // 只能放String
list.add("Hello");
// list.add(123); // 编译错误!类型安全
String s = list.get(0); // 无需强制转换
// 编译期类型检查,运行期更安全
}
}
| 好处 | 说明 |
|---|---|
| 类型安全 | 编译期检查类型,避免ClassCastException |
| 消除强转 | 不需要手动类型转换 |
| 代码复用 | 一份代码适用于多种类型 |
| 文档化 | 类型参数本身就是文档 |
二、泛型类
2.1 基本泛型类
java
// 单类型参数
public class Box<T> {
private T content;
public Box(T content) {
this.content = content;
}
public T getContent() {
return content;
}
public void setContent(T content) {
this.content = content;
}
@Override
public String toString() {
return "Box{" + content + "}";
}
}
// 使用
Box<String> stringBox = new Box<>("Hello");
String value = stringBox.getContent(); // 无需强转
Box<Integer> intBox = new Box<>(42);
int num = intBox.getContent(); // 自动拆箱
2.2 多类型参数
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; }
public void setKey(K key) { this.key = key; }
public void setValue(V value) { this.value = value; }
@Override
public String toString() {
return "(" + key + ", " + value + ")";
}
}
// 使用
Pair<String, Integer> nameAge = new Pair<>("Alice", 25);
Pair<Integer, Boolean> idActive = new Pair<>(1001, true);
2.3 实际应用:通用响应类
java
// API统一响应封装
public class ApiResponse<T> {
private int code;
private String message;
private T data;
private long timestamp;
private ApiResponse(int code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
this.timestamp = System.currentTimeMillis();
}
public static <T> ApiResponse<T> ok(T data) {
return new ApiResponse<>(200, "success", data);
}
public static <T> ApiResponse<T> error(String message) {
return new ApiResponse<>(500, message, null);
}
public static <T> ApiResponse<T> error(int code, String message) {
return new ApiResponse<>(code, message, null);
}
// getters...
}
// 使用
ApiResponse<User> response = ApiResponse.ok(new User("Alice", 25));
ApiResponse<List<Product>> listResponse = ApiResponse.ok(products);
ApiResponse<?> errorResponse = ApiResponse.error("服务器错误");
2.4 泛型类的继承
java
// 泛型父类
public abstract class Repository<T, ID> {
private Map<ID, T> store = new HashMap<>();
public void save(ID id, T entity) {
store.put(id, entity);
}
public T findById(ID id) {
return store.get(id);
}
public List<T> findAll() {
return new ArrayList<>(store.values());
}
}
// 具体子类(指定类型)
public class UserRepository extends Repository<User, String> {
public List<User> findByName(String name) {
return findAll().stream()
.filter(u -> u.getName().equals(name))
.collect(Collectors.toList());
}
}
// 保持泛型的子类
public abstract class CacheRepository<T, ID> extends Repository<T, ID> {
private Map<ID, T> cache = new HashMap<>();
@Override
public T findById(ID id) {
return cache.computeIfAbsent(id, super::findById);
}
}
三、泛型接口
3.1 基本泛型接口
java
// 比较器接口
public interface Comparator<T> {
int compare(T o1, T o2);
}
// 实现方式1:指定具体类型
public class StudentAgeComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return Integer.compare(o1.getAge(), o2.getAge());
}
}
// 实现方式2:保持泛型
public class ReverseComparator<T> implements Comparator<T> {
private final Comparator<T> original;
public ReverseComparator(Comparator<T> original) {
this.original = original;
}
@Override
public int compare(T o1, T o2) {
return original.compare(o2, o1); // 反转
}
}
3.2 实际应用:DAO模式
java
// 泛型DAO接口
public interface GenericDao<T, ID> {
T findById(ID id);
List<T> findAll();
void save(T entity);
void update(T entity);
void delete(ID id);
boolean exists(ID id);
}
// 具体实现
public class UserDao implements GenericDao<User, Long> {
@Override
public User findById(Long id) {
// 数据库查询逻辑
return null;
}
@Override
public List<User> findAll() {
return Collections.emptyList();
}
@Override
public void save(User entity) {
// 保存逻辑
}
@Override
public void update(User entity) {
// 更新逻辑
}
@Override
public void delete(Long id) {
// 删除逻辑
}
@Override
public boolean exists(Long id) {
return findById(id) != null;
}
}
四、泛型方法
4.1 基本泛型方法
java
public class GenericMethods {
// 泛型方法:<T>声明在返回类型之前
public static <T> void printArray(T[] array) {
for (T element : array) {
System.out.print(element + " ");
}
System.out.println();
}
// 泛型方法返回泛型类型
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> zipToMap(K[] keys, V[] values) {
if (keys.length != values.length) {
throw new IllegalArgumentException("keys和values长度必须一致");
}
Map<K, V> map = new HashMap<>();
for (int i = 0; i < keys.length; i++) {
map.put(keys[i], values[i]);
}
return map;
}
public static void main(String[] args) {
// 使用泛型方法
Integer[] nums = {1, 2, 3, 4, 5};
String[] strs = {"A", "B", "C"};
printArray(nums); // 类型推断
printArray(strs);
List<String> names = List.of("Alice", "Bob", "Charlie");
String first = getFirst(names); // 推断为String
String[] keys = {"name", "age", "city"};
String[] values = {"Alice", "25", "Beijing"};
Map<String, String> person = zipToMap(keys, values);
System.out.println(person);
}
}
4.2 静态泛型方法
java
public class CollectionUtils {
// 注意:静态方法必须声明自己的类型参数
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> List<T> distinct(List<T> list) {
return new ArrayList<>(new LinkedHashSet<>(list));
}
public static <T extends Comparable<T>> T max(List<T> list) {
if (list == null || list.isEmpty()) {
throw new IllegalArgumentException("列表不能为空");
}
T max = list.get(0);
for (T item : list) {
if (item.compareTo(max) > 0) {
max = item;
}
}
return max;
}
// 使用示例
public static void main(String[] args) {
List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<Integer> evens = filter(numbers, n -> n % 2 == 0);
System.out.println("偶数: " + evens); // [2, 4, 6, 8, 10]
Integer max = max(numbers);
System.out.println("最大值: " + max); // 10
}
}
五、类型通配符
5.1 无界通配符 <?>
java
// ? 表示未知类型
public static void printList(List<?> list) {
for (Object item : list) {
System.out.println(item);
}
}
// 使用
List<String> strings = List.of("A", "B");
List<Integer> integers = List.of(1, 2);
printList(strings); // OK
printList(integers); // OK
// List<?> vs List<Object>
List<?> unknownList = new ArrayList<String>(); // OK
// List<Object> objectList = new ArrayList<String>(); // 编译错误!
5.2 上界通配符 <? extends T>
java
// ? extends Number:Number或其子类
public static double sum(List<? extends Number> list) {
double total = 0;
for (Number num : list) {
total += num.doubleValue(); // 可以读取为Number
}
return total;
}
// 使用
List<Integer> ints = List.of(1, 2, 3);
List<Double> doubles = List.of(1.5, 2.5, 3.5);
System.out.println(sum(ints)); // 6.0
System.out.println(sum(doubles)); // 7.5
// 限制:不能添加元素(除了null)
public static void addNumber(List<? extends Number> list) {
// list.add(1); // 编译错误!不知道具体类型
// list.add(1.0); // 编译错误!
list.add(null); // OK,null是所有类型的合法值
}
5.3 下界通配符 <? super T>
java
// ? super Integer:Integer或其父类
public static void addIntegers(List<? super Integer> list) {
list.add(1);
list.add(2);
list.add(3);
// 可以添加Integer及其子类
}
// 使用
List<Number> numbers = new ArrayList<>();
addIntegers(numbers); // OK,Number是Integer的父类
System.out.println(numbers); // [1, 2, 3]
List<Object> objects = new ArrayList<>();
addIntegers(objects); // OK,Object是Integer的祖先
System.out.println(objects); // [1, 2, 3]
// 限制:读取只能是Object
public static void readFromList(List<? super Integer> list) {
Object obj = list.get(0); // 只能读取为Object
// Integer num = list.get(0); // 编译错误!
}
5.4 PECS原则(Producer Extends, Consumer Super)
java
// PECS原则:生产者用extends,消费者用super
// 生产者(提供数据):用 extends
public static <T> void copy(List<? extends T> src, List<? super T> dest) {
for (T item : src) { // 从src读取(生产者)
dest.add(item); // 向dest写入(消费者)
}
}
// 实际应用
List<Integer> source = List.of(1, 2, 3);
List<Number> destination = new ArrayList<>();
copy(source, destination); // Integer extends Number
System.out.println(destination); // [1, 2, 3]
// Collections.copy的签名
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
// src是生产者(extends),dest是消费者(super)
}
5.5 通配符对比表
| 通配符 | 含义 | 读 | 写 | 使用场景 |
|---|---|---|---|---|
<?> |
未知类型 | Object | 不能写 | 只读操作 |
<? extends T> |
T的子类 | T | 不能写 | 生产者(读取) |
<? super T> |
T的父类 | Object | T | 消费者(写入) |
六、泛型约束
6.1 有界类型参数
java
// T必须是Comparable的实现类
public static <T extends Comparable<T>> T findMax(List<T> list) {
T max = list.get(0);
for (T item : list) {
if (item.compareTo(max) > 0) {
max = item;
}
}
return max;
}
// 多重约束:T必须同时满足多个条件
public static <T extends Comparable<T> & Serializable> void process(T item) {
// T必须同时实现Comparable和Serializable
System.out.println(item.compareTo(item));
}
6.2 常见约束示例
java
// 约束为Number的子类
public class MathUtils {
public static <T extends Number> double average(List<T> numbers) {
double sum = 0;
for (T num : numbers) {
sum += num.doubleValue();
}
return sum / numbers.size();
}
}
// 约束为Enum的子类
public static <T extends Enum<T>> T parseEnum(Class<T> enumType, String name) {
return Enum.valueOf(enumType, name);
}
// 实际应用:Builder模式中的类型约束
public abstract class Builder<T extends Builder<T>> {
protected String name;
@SuppressWarnings("unchecked")
public T withName(String name) {
this.name = name;
return (T) this; // 返回子类类型
}
public abstract T self();
}
public class UserBuilder extends Builder<UserBuilder> {
private int age;
public UserBuilder withAge(int age) {
this.age = age;
return this;
}
@Override
public UserBuilder self() {
return this;
}
public User build() {
return new User(name, age);
}
}
// 链式调用
User user = new UserBuilder()
.withName("Alice")
.withAge(25)
.build();
七、类型擦除
7.1 什么是类型擦除?
Java的泛型是编译期特性,在运行时会被擦除(Type Erasure)。
java
// 编译前(源代码)
List<String> strings = new ArrayList<>();
List<Integer> integers = new ArrayList<>();
// 编译后(字节码)
List strings = new ArrayList();
List integers = new ArrayList();
// 运行时无法区分
System.out.println(strings.getClass() == integers.getClass()); // true
7.2 类型擦除的规则
java
// 规则1:无界泛型擦除为Object
public class Box<T> {
private T value;
// 编译后:
// private Object value;
}
// 规则2:有界泛型擦除为上界
public class NumberBox<T extends Number> {
private T value;
// 编译后:
// private Number value;
}
// 规则3:调用泛型方法时插入强制转换
List<String> list = new ArrayList<>();
list.add("Hello");
String s = list.get(0);
// 编译后:
// list.add("Hello");
// String s = (String) list.get(0); // 插入强转
7.3 类型擦除的限制
java
// 限制1:不能使用基本类型
// List<int> list; // 编译错误!必须用List<Integer>
// 限制2:不能instanceof泛型类型
public <T> void check(Object obj) {
// if (obj instanceof T) {} // 编译错误!
// 解决方案:传递Class对象
}
// 限制3:不能创建泛型数组
// T[] array = new T[10]; // 编译错误!
// 解决方案:使用Array.newInstance
public <T> T[] createArray(Class<T> componentType, int size) {
@SuppressWarnings("unchecked")
T[] array = (T[]) Array.newInstance(componentType, size);
return array;
}
// 限制4:不能在catch中使用泛型异常
// catch (T e) {} // 编译错误!
7.4 桥接方法
java
// 泛型方法重写时的桥接方法
public interface Processor<T> {
void process(T item);
}
public class StringProcessor implements Processor<String> {
@Override
public void process(String item) {
System.out.println("Processing: " + item);
}
// 编译器生成桥接方法:
// public void process(Object item) {
// process((String) item);
// }
}
八、泛型的高级用法
8.1 递归类型约束
java
// 常见于Builder模式和枚举中
public abstract class Enum<E extends Enum<E>> {
private final String name;
private final int ordinal;
protected Enum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}
public final int compareTo(E o) {
// 自己和自己比较
}
}
// Builder模式
public abstract class Builder<T extends Builder<T>> {
@SuppressWarnings("unchecked")
public T self() {
return (T) this;
}
}
8.2 泛型与反射
java
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
public abstract class GenericDao<T> {
private final Class<T> entityClass;
@SuppressWarnings("unchecked")
public GenericDao() {
// 通过反射获取泛型的实际类型
Type genericSuperclass = getClass().getGenericSuperclass();
ParameterizedType paramType = (ParameterizedType) genericSuperclass;
this.entityClass = (Class<T>) paramType.getActualTypeArguments()[0];
}
public T newInstance() throws InstantiationException, IllegalAccessException {
return entityClass.newInstance();
}
public Class<T> getEntityClass() {
return entityClass;
}
}
// 使用
public class UserDao extends GenericDao<User> {
// entityClass = User.class
}
// 运行时获取泛型信息
UserDao dao = new UserDao();
System.out.println(dao.getEntityClass()); // class User
九、常见面试题解析
面试题1:泛型的类型擦除是什么?
答:Java泛型是编译期特性,编译后类型参数会被擦除。无界泛型擦除为Object,有界泛型擦除为上界。运行时无法获取泛型的实际类型参数。
面试题2:List<String>和List<Integer>在运行时有区别吗?
答 :没有区别。由于类型擦除,两者在运行时都是List。List<String>.class == List<Integer>.class为true。
面试题3:<? extends T>和<? super T>的区别?
| 特征 | <? extends T> |
<? super T> |
|---|---|---|
| 读取 | T类型 | Object类型 |
| 写入 | 不能写入 | T类型 |
| 场景 | 生产者 | 消费者 |
| 记忆 | 上界通配符 | 下界通配符 |
面试题4:为什么不能创建泛型数组?
java
// 因为数组在运行时知道元素类型,泛型在运行时被擦除
// 如果允许,会导致类型安全问题
T[] array = new T[10]; // 假设T=String
Object[] objArray = array;
objArray[0] = 123; // 运行时不会报错(因为实际是Object[])
String s = array[0]; // ClassCastException!
// 正确做法
T[] array = (T[]) new Object[10]; // 有unchecked警告
// 或者使用Class<T>创建
十、总结与下篇预告
本篇核心要点
| 要点 | 说明 |
|---|---|
| 泛型类 | class Box<T>,类型参数化 |
| 泛型方法 | <T> void method(T t),方法级泛型 |
| 泛型接口 | interface Repository<T, ID> |
| 通配符 | <?>、<? extends T>、<? super T> |
| PECS原则 | Producer Extends, Consumer Super |
| 类型擦除 | 编译期特性,运行时擦除 |
| 有界约束 | <T extends Comparable<T>> |
🤔 互动问题
- 为什么Java选择类型擦除而不是具体化泛型(reified generics)?
- Kotlin的泛型和Java的泛型有什么区别?
Class<T>在泛型编程中有什么重要作用?
📖 下篇预告
下一篇我们将学习**《IO流:字节流与字符流》**,深入了解Java的IO体系结构、InputStream/OutputStream、Reader/Writer的使用。