深入讲解 Java 泛型
在基础泛型的使用上,你已经了解了泛型类、泛型方法、泛型接口、通配符以及类型擦除等内容。接下来,我们将进一步深入探讨 Java 泛型的高级特性、泛型在反射中的应用、泛型与数组、泛型的运行时行为,以及泛型在数据库操作和 Spring 框架中的应用。
1. 泛型的高级特性
1.1 泛型的嵌套
泛型可以嵌套使用,例如 List<List<String>>:
import java.util.*;
public class Test {
public static void main(String[] args) {
List<List<String>> list = new ArrayList<>();
List<String> innerList = new ArrayList<>();
innerList.add("Hello");
list.add(innerList);
System.out.println(list.get(0).get(0)); // 输出 Hello
}
}
嵌套泛型在处理复杂数据结构(如 Map<String, List<Integer>>)时非常有用。
1.2 泛型与继承
在 Java 中,泛型不能被继承,但可以进行类型协变(Covariance)与逆变(Contravariance):
List<String> stringList = new ArrayList<>();
List<Object> objectList = stringList; // 编译错误!
泛型是不变的,List<String> 不是 List<Object> 的子类,需要使用 通配符:
List<? extends Object> list = new ArrayList<String>(); // 允许
| 泛型通配符 | 作用 |
|---|---|
<? extends T> | 适用于 读取数据,不能写入(除了 null) |
<? super T> | 适用于 写入数据,可以安全写入 T 类型 |
示例:
import java.util.*;
public class Test {
public static void main(String[] args) {
List<? extends Number> numList = new ArrayList<Integer>();
// numList.add(100); // 编译错误(不能写入)
List<? super Integer> intList = new ArrayList<Number>();
intList.add(100); // 允许写入 Integer 或其子类
}
}
1.3 泛型方法的多重约束
你可以对泛型参数添加多个约束:
// T 必须是 Number 的子类,并且实现 Comparable 接口
public static <T extends Number & Comparable<T>> T findMax(T a, T b) {
return a.compareTo(b) > 0 ? a : b;
}
public class Test {
public static void main(String[] args) {
System.out.println(findMax(10, 20)); // 输出 20
}
}
注意:
- Java 只支持单继承,但可以同时实现多个接口。
T extends A & B中 A 必须是类,B 必须是接口。
2. 泛型与反射
2.1 运行时获取泛型类型
由于类型擦除(Type Erasure),在运行时无法直接获取泛型类型:
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void main(String[] args) {
List<String> list1 = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();
System.out.println(list1.getClass() == list2.getClass()); // true
}
}
解决方案:使用反射获取泛型参数
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
class GenericClass<T> {}
public class Test {
public static void main(String[] args) {
GenericClass<String> obj = new GenericClass<String>() {};
Type type = obj.getClass().getGenericSuperclass();
if (type instanceof ParameterizedType) {
Type[] actualTypeArgs = ((ParameterizedType) type).getActualTypeArguments();
System.out.println(actualTypeArgs[0]); // class java.lang.String
}
}
}
2.2 反射创建泛型实例
如果泛型类中需要使用反射创建对象:
class Container<T> {
private Class<T> type;
public Container(Class<T> type) {
this.type = type;
}
public T createInstance() throws InstantiationException, IllegalAccessException {
return type.newInstance();
}
}
public class Test {
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
Container<String> container = new Container<>(String.class);
String str = container.createInstance();
System.out.println(str);
}
}
注意:
newInstance()需要无参构造函数。
3. 泛型与数组
3.1 泛型数组的限制
在 Java 中,不能直接创建泛型数组:
T[] array = new T[10]; // 编译错误
解决方案:
@SuppressWarnings("unchecked")
T[] array = (T[]) new Object[10];
或者使用 Array.newInstance():
import java.lang.reflect.Array;
public class Test {
public static <T> T[] createArray(Class<T> clazz, int size) {
return (T[]) Array.newInstance(clazz, size);
}
public static void main(String[] args) {
String[] arr = createArray(String.class, 5);
System.out.println(arr.length); // 输出 5
}
}
4. 泛型在数据库操作中的应用
泛型在 DAO(Data Access Object)模式 中被广泛使用:
import java.util.List;
interface GenericDAO<T> {
void save(T entity);
T findById(int id);
List<T> findAll();
}
class User {
private int id;
private String name;
public User(int id, String name) {
this.id = id;
this.name = name;
}
public String getName() {
return name;
}
}
class UserDAO implements GenericDAO<User> {
@Override
public void save(User user) {
System.out.println("Saving user: " + user.getName());
}
@Override
public User findById(int id) {
return new User(id, "Mock User");
}
@Override
public List<User> findAll() {
return List.of(new User(1, "Alice"), new User(2, "Bob"));
}
}
public class Test {
public static void main(String[] args) {
UserDAO userDAO = new UserDAO();
userDAO.save(new User(1, "Charlie"));
User user = userDAO.findById(1);
System.out.println(user.getName());
}
}
5. 泛型在 Spring 框架中的应用
5.1 Spring 中的泛型依赖注入
Spring 允许通过泛型自动注入适配的 Bean:
@Component
public class GenericService<T> {
public void printType(T obj) {
System.out.println(obj.getClass().getName());
}
}
@Service
public class UserService extends GenericService<User> {}
public class Test {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = context.getBean(UserService.class);
userService.printType(new User(1, "Alice"));
}
}
Spring 通过泛型参数解析,自动为 UserService 注入 User 类型。
总结
| 知识点 | 说明 |
|---|---|
| 泛型继承 | 泛型是不变的,List<String> 不是 List<Object> |
| 运行时反射 | 通过 ParameterizedType 获取泛型参数 |
| 泛型数组 | 不能直接创建,需 Array.newInstance() |
| 泛型 DAO | 泛型应用于数据库操作,提升代码复用性 |
| 泛型与 Spring | 用于自动注入和通用服务 |
更多详细内容请关注其他相关文章。