泛型详解
泛型的作用
Java 5 引入泛型,主要作用:
- 类型安全:编译期检查类型错误
- 消除强制转换:自动类型转换
- 提高代码复用:编写通用代码
// 没有泛型
List raw = new ArrayList();
raw.add("通知");
raw.add(2024); // 可加入不同类型
String s1 = (String) raw.get(0); // 需要强转
String s2 = (String) raw.get(1); // 运行时异常
// 使用泛型
List<String> msgs = new ArrayList<>();
msgs.add("通知");
// msgs.add(2024); // 编译错误:类型不匹配
String ok = msgs.get(0); // 无需强转
泛型的使用方式
1. 泛型类
// 邮件投递箱:支持任意类型的消息载荷
public class Mailbox<T> {
private T payload;
public void put(T payload) {
this.payload = payload;
}
public T take() {
return payload;
}
}
// 使用
Mailbox<String> textBox = new Mailbox<>();
textBox.put("欢迎使用");
String msg = textBox.take();
Mailbox<Integer> idBox = new Mailbox<>();
idBox.put(100);
int id = idBox.take();
2. 泛型接口
public interface Comparable<T> {
int compareTo(T other);
}
// 指定类型:按人口比较城市
public class City implements Comparable<City> {
private final String name;
private final int population;
public City(String name, int population) {
this.name = name;
this.population = population;
}
@Override
public int compareTo(City other) {
return Integer.compare(this.population, other.population);
}
}
// 保留泛型:用于任意可比较类型
public class Wrapper<T extends Comparable<T>> implements Comparable<T> {
private final T value;
public Wrapper(T value) { this.value = value; }
@Override
public int compareTo(T other) {
return value.compareTo(other);
}
}
3. 泛型方法
public class SensorUtil {
// 泛型方法:打印读数
public static <T> void printReadings(T[] readings) {
for (T r : readings) {
System.out.print(r + " ");
}
System.out.println();
}
public static void main(String[] args) {
Double[] temps = {23.5, 24.0, 22.9};
String[] events = {"open", "close"};
printReadings(temps);
printReadings(events);
}
}
注意:静态泛型方法不能使用类的泛型参数
public class MyClass<T> {
// 错误:静态方法不能使用类的 T
// public static void method(T param) { }
// 正确:声明自己的泛型参数
public static <E> void method(E param) { }
}
项目中泛型的应用
// 1. 通用响应体
public class Response<T> {
private int code;
private String message;
private T data;
public static <T> Response<T> ok(T data) {
Response<T> r = new Response<>();
r.code = 200;
r.message = "OK";
r.data = data;
return r;
}
public static <T> Response<T> fail(String msg) {
Response<T> r = new Response<>();
r.code = 500;
r.message = msg;
return r;
}
}
// 使用
Response<Integer> count = Response.ok(42);
Response<List<String>> names = Response.ok(Arrays.asList("Alice", "Bob"));
// 2. 通用仓库
public class Repository<T> {
private final List<T> store = new ArrayList<>();
public void save(T item) { store.add(item); }
public List<T> findAll() { return new ArrayList<>(store); }
}
Repository<String> logs = new Repository<>();
logs.save("系统启动");
List<String> all = logs.findAll();
常用类型参数约定
T: 类型(Type)E: 元素(Element)K/V: 键/值(Key/Value)N: 数值(Number)?: 未知类型(通配符)S/U/...: 另一占位类型名,语义与T类似
协变、逆变与不变
- 数组是协变的:
Object[]可以接收String[] - 泛型默认不变:
List<Object>与List<String>无继承关系 - 使用通配符可表达读/写意图
通配符与 Object 的区别
List<?>: 可读不可写(除null),可接收任何具体泛型的ListList<Object>: 可读可写,但不能接收List<String>等具体泛型
List<?> any = new ArrayList<String>();
Object x = ((List<?>) any).get(0); // 可读
// any.add("x"); // 不允许写入
List<Object> objs = new ArrayList<>();
objs.add(123); // 可写
// List<String> ss = objs; // 编译错误
反射绕过泛型检查(示例)
// 将非整数值塞进 List<Integer> (不推荐,仅示例)
List<Integer> ids = new ArrayList<>();
Method m = ids.getClass().getMethod("add", Object.class);
m.invoke(ids, UUID.randomUUID().toString());
System.out.println(ids.get(0));
边界通配符: extends 与 super
? extends T: 上界,适合读取? super T: 下界,适合写入- PECS 原则: Producer-Extends, Consumer-Super
// 事件队列:读取与写入
class Event {}
class LoginEvent extends Event {}
// 上界:只读
void readEvents(List<? extends Event> events) {
Event e = events.get(0);
}
// 下界:只写
void publishLogin(List<? super LoginEvent> outbox) {
outbox.add(new LoginEvent());
}
类型擦除机制
- 编译期擦除类型参数,将
T替换为Object或上界类型 - 运行期不保留具体泛型信息(非可重新化类型)
// 泛型版本
public class Repository<T> {
T data;
void save(T value) { this.data = value; }
}
// 擦除后等价
public class Repository_Erased {
Object data;
void save(Object value) { this.data = value; }
}
受类型擦除影响的限制
- 不能创建泛型数组:
new List<String>[10](编译错误) - 不能捕获泛型异常多次分支
- 静态上下文不可引用类的类型参数
- 不能直接
new T()或T.class
// 通过传入 Class<T> 解决 new T() 问题
class Factory<T> {
private final Class<T> type;
Factory(Class<T> type) { this.type = type; }
T create() throws Exception { return type.getDeclaredConstructor().newInstance(); }
}