深入讲解 Java 泛型
                           
天天向上
发布: 2025-03-03 23:06:52

原创
451 人浏览过

在基础泛型的使用上,你已经了解了泛型类、泛型方法、泛型接口、通配符以及类型擦除等内容。接下来,我们将进一步深入探讨 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 & BA 必须是类,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用于自动注入和通用服务

更多详细内容请关注其他相关文章。

发表回复 0

Your email address will not be published. Required fields are marked *