一、泛型:编译时的类型契约
泛型本质是类型参数化,提供编译时类型检查,避免强制转换错误。但Java采用类型擦除实现,运行时类型参数信息被擦除(如List<String>变为List)。
java
// 泛型类定义
public class Box<T> {
private T content;
public void setContent(T content) { this.content = content; }
public T getContent() { return content; }
}
// 使用 - 编译时检查
Box<String> stringBox = new Box<>();
stringBox.setContent("Hello");
// stringBox.setContent(100); // 编译错误!
二、反射:运行时的类型手术刀
反射java.lang.reflect允许在运行时分析/操作类、方法、字段等元信息,突破编译时限制。
java
// 获取类信息
Class<?> clazz = Class.forName("com.example.Box");
// 创建实例
Object objBox = clazz.getDeclaredConstructor().newInstance();
// 获取setContent方法
Method setMethod = clazz.getMethod("setContent", Object.class);
// 反射调用 - 绕过泛型检查!
setMethod.invoke(objBox, 100); // 运行时放入Integer,编译通过但隐患!
三、泛型遇上反射:类型擦除的困境与突破
核心冲突:反射在运行时无法直接获取泛型的具体类型参数(如T是String还是Integer),因为已被擦除。
解决方案:通过泛型签名(存在于字节码)获取元数据:
java
public class GenericBox<T> {
private List<T> itemList = new ArrayList<>();
public void addItem(T item) {
itemList.add(item);
}
}
// 反射获取泛型参数类型
Field field = GenericBox.class.getDeclaredField("itemList");
Type genericType = field.getGenericType(); // 关键!
if (genericType instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) genericType;
Type[] actualTypes = pt.getActualTypeArguments(); // 获取真实类型参数
System.out.println(actualTypes[0]); // 输出: T (但运行时仍是类型变量)
}
获取具体化类型:需在子类/匿名类中固定泛型参数:
java
// 创建固定泛型类型的子类(匿名类)
GenericBox<String> box = new GenericBox<String>() {};
Type genericSuperClass = box.getClass().getGenericSuperclass();
ParameterizedType pt = (ParameterizedType) genericSuperClass;
Type actualType = pt.getActualTypeArguments()[0];
System.out.println(actualType); // 输出: class java.lang.String
四、实战应用:动态注入框架
利用反射+泛型实现灵活组件管理:
java
// 泛型仓库接口
public interface Repository<T> {
void save(T entity);
}
// 反射动态实现
public class DynamicRepository<T> implements Repository<T> {
private Class<T> entityType;
public DynamicRepository(Class<T> entityType) {
this.entityType = entityType; // 保留具体类型
}
@Override
public void save(T entity) {
// 利用entityType进行数据库操作(如Hibernate Session)
System.out.println("Saving " + entityType.getSimpleName() + ": " + entity);
}
}
// 使用示例
Repository<User> userRepo = new DynamicRepository<>(User.class);
userRepo.save(new User("Alice"));
💡 调试技巧:使用javap -v YourClass.class查看字节码中的Signature属性,这是泛型信息的存储位置。
结语:平衡的艺术
泛型与反射是Java强大而危险的双刃剑:
- 泛型:保障编译安全,代价是运行时类型擦除;
- 反射:突破静态限制,风险是绕过安全检查与性能损耗。
掌握ParameterizedType、TypeVariable等接口,理解类型擦除边界,方能在框架设计(如Spring DI)与动态编程中游刃有余。谨慎使用反射破坏泛型约束,这往往意味着设计缺陷。

被折叠的 条评论
为什么被折叠?



