Java 泛型详解

1. 泛型概述

1.1 什么是泛型

泛型(Generics)是JDK 5引入的特性,允许在定义类、接口和方法时使用类型参数,提供编译时类型安全检查,避免运行时类型转换异常。

1.2 泛型的好处

  1. 类型安全:编译时检查类型

  2. 消除强制转换:代码更简洁

  3. 代码重用:编写通用的算法和数据结构

  4. 更好的可读性:明确类型约束

2. 泛型基础语法

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;
    }
}

// 多个类型参数
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; }
}

// 使用示例
Box<String> stringBox = new Box<>("Hello");
Box<Integer> intBox = new Box<>(100);
Pair<String, Integer> pair = new Pair<>("age", 25);

2.2 泛型接口

java 复制代码
// 定义泛型接口
public interface Repository<T> {
    void save(T entity);
    T findById(String id);
    List<T> findAll();
}

// 实现泛型接口(指定具体类型)
public class UserRepository implements Repository<User> {
    @Override
    public void save(User entity) { /* 实现 */ }
    
    @Override
    public User findById(String id) { /* 实现 */ }
    
    @Override
    public List<User> findAll() { /* 实现 */ }
}

// 实现泛型接口(保持泛型)
public class GenericRepository<T> implements Repository<T> {
    @Override
    public void save(T entity) { /* 实现 */ }
    
    @Override
    public T findById(String id) { /* 实现 */ }
    
    @Override
    public List<T> findAll() { /* 实现 */ }
}

2.3 泛型方法

java 复制代码
public class GenericMethods {
    
    // 普通类中的泛型方法
    public <T> void printArray(T[] array) {
        for (T element : array) {
            System.out.print(element + " ");
        }
        System.out.println();
    }
    
    // 带返回值的泛型方法
    public <T> T getFirstElement(List<T> list) {
        if (list == null || list.isEmpty()) {
            return null;
        }
        return list.get(0);
    }
    
    // 多个类型参数的泛型方法
    public <K, V> void printPair(K key, V value) {
        System.out.println(key + " -> " + value);
    }
    
    // 静态泛型方法
    public static <T> boolean isNull(T obj) {
        return obj == null;
    }
}

// 使用示例
GenericMethods gm = new GenericMethods();
String[] strings = {"A", "B", "C"};
Integer[] numbers = {1, 2, 3};

gm.printArray(strings);  // 类型推断
gm.<String>printArray(strings);  // 显式指定类型
gm.printArray(numbers);

3. 类型参数约束

3.1 上界通配符(extends)

java 复制代码
// 类型参数必须是Number或其子类
public class NumberContainer<T extends Number> {
    private T number;
    
    public NumberContainer(T number) {
        this.number = number;
    }
    
    public double getSquare() {
        return number.doubleValue() * number.doubleValue();
    }
}

// 实现多个接口
public class MultiBound<T extends Number & Comparable<T> & Serializable> {
    // T必须是Number的子类,并且实现Comparable和Serializable接口
}

// 使用示例
NumberContainer<Integer> intContainer = new NumberContainer<>(10);
NumberContainer<Double> doubleContainer = new NumberContainer<>(3.14);
// NumberContainer<String> stringContainer; // 编译错误

3.2 通配符(Wildcards)

3.2.1 无界通配符(?)
java 复制代码
public class WildcardDemo {
    // 接受任何类型的List
    public static void printList(List<?> list) {
        for (Object obj : list) {
            System.out.println(obj);
        }
    }
    
    // 只能获取为Object,不能添加(除了null)
    public static void processList(List<?> list) {
        // list.add("string"); // 编译错误
        list.add(null); // 可以添加null
        
        Object obj = list.get(0); // 可以读取为Object
    }
}
3.2.2 上界通配符(? extends)
java 复制代码
public class UpperBoundedWildcard {
    // 接受Number及其子类的List
    public static double sumOfList(List<? extends Number> list) {
        double sum = 0.0;
        for (Number num : list) {
            sum += num.doubleValue();
        }
        return sum;
    }
    
    // 可以读取为Number,不能添加
    public static void processNumbers(List<? extends Number> list) {
        Number num = list.get(0); // 可以读取
        
        // 以下都会编译错误
        // list.add(new Integer(10));
        // list.add(new Double(3.14));
        // list.add(new Object());
    }
}
3.2.3 下界通配符(? super)
java 复制代码
public class LowerBoundedWildcard {
    // 接受Integer及其父类的List
    public static void addNumbers(List<? super Integer> list) {
        for (int i = 1; i <= 5; i++) {
            list.add(i);
        }
    }
    
    // 可以添加Integer及其子类,读取为Object
    public static void processIntegers(List<? super Integer> list) {
        list.add(10); // 可以添加Integer
        list.add(Integer.valueOf(20)); // 可以添加Integer
        
        // list.add(new Object()); // 编译错误
        
        Object obj = list.get(0); // 只能读取为Object
        // Integer num = list.get(0); // 编译错误
    }
}

4. 类型擦除与桥方法

4.1 类型擦除原理

java 复制代码
// 编译前
public class GenericClass<T> {
    private T value;
    
    public void setValue(T value) {
        this.value = value;
    }
    
    public T getValue() {
        return value;
    }
}

// 编译后(类型擦除)
public class GenericClass {
    private Object value;
    
    public void setValue(Object value) {
        this.value = value;
    }
    
    public Object getValue() {
        return value;
    }
}

// 有界类型的擦除
public class BoundedClass<T extends Number> {
    private T value;
    
    public void setValue(T value) {
        this.value = value;
    }
    
    public T getValue() {
        return value;
    }
}

// 编译后
public class BoundedClass {
    private Number value;
    
    public void setValue(Number value) {
        this.value = value;
    }
    
    public Number getValue() {
        return value;
    }
}

4.2 桥方法(Bridge Methods)

java 复制代码
// 编译前
public class Node<T> {
    public T data;
    
    public void setData(T data) {
        this.data = data;
    }
}

public class IntegerNode extends Node<Integer> {
    @Override
    public void setData(Integer data) {
        super.setData(data);
    }
}

// 编译后会产生桥方法
public class IntegerNode extends Node {
    // 重写的方法
    public void setData(Integer data) {
        super.setData(data);
    }
    
    // 编译器生成的桥方法
    public void setData(Object data) {
        setData((Integer) data); // 类型检查和转换
    }
}

5. 泛型与数组

5.1 不能创建泛型数组

java 复制代码
public class ArrayIssues {
    // 以下代码编译错误
    // List<String>[] arrayOfLists = new List<String>[10];
    
    // 可以创建通配符类型的数组
    List<?>[] arrayOfLists = new List<?>[10];
    
    // 可以使用ArrayList代替数组
    List<List<String>> listOfLists = new ArrayList<>();
    
    // 类型安全的替代方案
    @SuppressWarnings("unchecked")
    public <T> T[] createGenericArray(Class<T> type, int size) {
        return (T[]) Array.newInstance(type, size);
    }
}

5.2 可变参数与泛型

java 复制代码
public class VarargsAndGenerics {
    // 可变参数实际上是数组,会有类型安全问题
    @SafeVarargs  // JDK 7+,表示方法不会对可变参数执行不安全操作
    public static <T> List<T> createList(T... elements) {
        List<T> list = new ArrayList<>();
        for (T element : elements) {
            list.add(element);
        }
        return list;
    }
    
    // 使用示例
    List<String> strings = createList("A", "B", "C");
    List<Integer> numbers = createList(1, 2, 3);
}

6. 实际应用示例

6.1 泛型缓存

java 复制代码
public class GenericCache<K, V> {
    private final Map<K, V> cache = new ConcurrentHashMap<>();
    
    public void put(K key, V value) {
        cache.put(key, value);
    }
    
    public V get(K key) {
        return cache.get(key);
    }
    
    public boolean contains(K key) {
        return cache.containsKey(key);
    }
    
    public void remove(K key) {
        cache.remove(key);
    }
    
    public Set<K> keySet() {
        return cache.keySet();
    }
}

6.2 泛型DAO模式

java 复制代码
// 基础实体接口
public interface Entity {
    Long getId();
    void setId(Long id);
}

// 泛型DAO接口
public interface GenericDAO<T extends Entity> {
    void save(T entity);
    void update(T entity);
    void delete(Long id);
    T findById(Long id);
    List<T> findAll();
    List<T> findByCriteria(String criteria, Object... params);
}

// 泛型DAO实现
public abstract class GenericDAOImpl<T extends Entity> implements GenericDAO<T> {
    protected Class<T> entityClass;
    
    public GenericDAOImpl(Class<T> entityClass) {
        this.entityClass = entityClass;
    }
    
    @Override
    public T findById(Long id) {
        // 使用反射创建实例
        try {
            T entity = entityClass.getDeclaredConstructor().newInstance();
            entity.setId(id);
            // 实际应该从数据库加载
            return entity;
        } catch (Exception e) {
            throw new RuntimeException("Failed to create entity", e);
        }
    }
    
    // 其他方法实现...
}

// 具体实体类
public class User implements Entity {
    private Long id;
    private String name;
    private String email;
    
    // getters and setters...
}

// 具体DAO
public class UserDAO extends GenericDAOImpl<User> {
    public UserDAO() {
        super(User.class);
    }
    
    // 可以添加User特有的方法
    public User findByEmail(String email) {
        // 实现...
        return null;
    }
}

6.3 泛型工厂模式

java 复制代码
// 产品接口
public interface Product {
    void use();
}

// 具体产品
public class Computer implements Product {
    @Override
    public void use() {
        System.out.println("Using computer");
    }
}

public class Phone implements Product {
    @Override
    public void use() {
        System.out.println("Using phone");
    }
}

// 泛型工厂接口
public interface Factory<T extends Product> {
    T create();
}

// 具体工厂
public class ComputerFactory implements Factory<Computer> {
    @Override
    public Computer create() {
        return new Computer();
    }
}

public class PhoneFactory implements Factory<Phone> {
    @Override
    public Phone create() {
        return new Phone();
    }
}

// 使用工厂
public class FactoryClient {
    public static void main(String[] args) {
        Factory<Computer> computerFactory = new ComputerFactory();
        Computer computer = computerFactory.create();
        computer.use();
        
        Factory<Phone> phoneFactory = new PhoneFactory();
        Phone phone = phoneFactory.create();
        phone.use();
    }
}

7. 泛型的最佳实践

7.1 命名约定

  • E - Element(集合中的元素)

  • K - Key(键)

  • V - Value(值)

  • N - Number(数字)

  • T - Type(类型)

  • S, U, V - 第二、第三、第四类型

7.2 使用建议

java 复制代码
public class GenericBestPractices {
    
    // 1. 优先使用泛型方法,而不是原始类型
    // 不好
    public static List getRawList() {
        return new ArrayList();
    }
    
    // 好
    public static <T> List<T> getGenericList() {
        return new ArrayList<>();
    }
    
    // 2. 使用有界通配符增加API灵活性
    // 接受任何Number子类的集合
    public static double sum(Collection<? extends Number> numbers) {
        return numbers.stream()
            .mapToDouble(Number::doubleValue)
            .sum();
    }
    
    // 3. 避免在静态成员中使用类型参数
    // 错误:静态成员不能使用类的类型参数
    // private static T staticField;
    
    // 正确:静态方法可以使用自己的类型参数
    public static <T> T firstElement(List<T> list) {
        return list.get(0);
    }
    
    // 4. 使用Class<T>参数进行类型安全的实例化
    public static <T> T createInstance(Class<T> clazz) throws Exception {
        return clazz.getDeclaredConstructor().newInstance();
    }
    
    // 5. 合理使用@SuppressWarnings注解
    @SuppressWarnings("unchecked")
    public <T> T[] toArray(List<T> list) {
        // 已知安全的类型转换
        return (T[]) list.toArray();
    }
}

7.3 常见陷阱与解决方案

java 复制代码
public class GenericPitfalls {
    
    // 陷阱1:不能实例化类型参数
    public <T> void createInstanceError() {
        // T obj = new T(); // 编译错误
    }
    
    // 解决方案:使用Class对象
    public <T> T createInstance(Class<T> clazz) throws Exception {
        return clazz.newInstance();
    }
    
    // 陷阱2:不能创建泛型数组
    public <T> void createArrayError() {
        // T[] array = new T[10]; // 编译错误
    }
    
    // 解决方案1:使用ArrayList
    public <T> List<T> createList() {
        return new ArrayList<>();
    }
    
    // 解决方案2:使用类型安全的反射
    public <T> T[] createArray(Class<T> type, int size) {
        @SuppressWarnings("unchecked")
        T[] array = (T[]) Array.newInstance(type, size);
        return array;
    }
    
    // 陷阱3:静态上下文不能使用类型参数
    // 解决方案:在静态方法中定义自己的类型参数
    public static <T> boolean isEmpty(Collection<T> collection) {
        return collection == null || collection.isEmpty();
    }
}

8. JDK中的泛型应用

8.1 集合框架中的泛型

java 复制代码
public class CollectionsExample {
    public static void main(String[] args) {
        // List使用泛型
        List<String> stringList = new ArrayList<>();
        stringList.add("Hello");
        stringList.add("World");
        // stringList.add(123); // 编译错误
        
        // Map使用泛型
        Map<String, Integer> wordCount = new HashMap<>();
        wordCount.put("apple", 5);
        wordCount.put("banana", 3);
        
        // Set使用泛型
        Set<Integer> numberSet = new HashSet<>();
        numberSet.add(1);
        numberSet.add(2);
        numberSet.add(3);
        
        // 使用Collections工具类
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        List<Integer> synchronizedList = Collections.synchronizedList(numbers);
        
        // 使用Stream API(大量使用泛型)
        List<String> filtered = stringList.stream()
            .filter(s -> s.length() > 3)
            .collect(Collectors.toList());
    }
}

8.2 函数式接口与泛型

java 复制代码
public class FunctionalGenerics {
    
    // 自定义泛型函数式接口
    @FunctionalInterface
    public interface Transformer<T, R> {
        R transform(T input);
    }
    
    // 使用示例
    public static void main(String[] args) {
        // 字符串转整数
        Transformer<String, Integer> stringToInt = Integer::parseInt;
        
        // 整数转字符串
        Transformer<Integer, String> intToString = String::valueOf;
        
        // 使用Stream的map方法(泛型应用)
        List<String> numbers = Arrays.asList("1", "2", "3");
        List<Integer> ints = numbers.stream()
            .map(stringToInt::transform)
            .collect(Collectors.toList());
            
        System.out.println(ints); // [1, 2, 3]
    }
}

9. 高级主题:泛型与反射

java 复制代码
public class GenericReflection {
    
    // 获取泛型类型信息
    public static void analyzeGenericType() {
        // 创建带泛型的List
        List<String> stringList = new ArrayList<>();
        
        // 获取Class对象
        Class<?> listClass = stringList.getClass();
        System.out.println("Class: " + listClass.getName()); // java.util.ArrayList
        
        // 获取泛型父类信息
        Type genericSuperclass = listClass.getGenericSuperclass();
        if (genericSuperclass instanceof ParameterizedType) {
            ParameterizedType pt = (ParameterizedType) genericSuperclass;
            Type[] actualTypeArguments = pt.getActualTypeArguments();
            System.out.println("Generic superclass type arguments: " + 
                             Arrays.toString(actualTypeArguments));
        }
        
        // 获取泛型接口信息
        Type[] genericInterfaces = listClass.getGenericInterfaces();
        for (Type type : genericInterfaces) {
            if (type instanceof ParameterizedType) {
                ParameterizedType pt = (ParameterizedType) type;
                System.out.println("Interface: " + pt.getRawType());
                System.out.println("Type arguments: " + 
                                 Arrays.toString(pt.getActualTypeArguments()));
            }
        }
    }
    
    // 创建泛型类型的实例
    public static <T> T createGenericInstance(Class<T> clazz) throws Exception {
        return clazz.getDeclaredConstructor().newInstance();
    }
    
    // 处理泛型方法
    public static class GenericMethodHolder {
        public <T> T genericMethod(Class<T> clazz) throws Exception {
            return clazz.getDeclaredConstructor().newInstance();
        }
    }
    
    // 获取方法的泛型返回类型
    public static void analyzeMethodReturnType() throws Exception {
        Method method = GenericMethodHolder.class.getMethod("genericMethod", Class.class);
        Type returnType = method.getGenericReturnType();
        
        if (returnType instanceof TypeVariable) {
            TypeVariable<?> typeVariable = (TypeVariable<?>) returnType;
            System.out.println("Return type is type variable: " + typeVariable.getName());
            System.out.println("Bounds: " + Arrays.toString(typeVariable.getBounds()));
        }
    }
}

10. 总结

10.1 核心要点

  1. 类型安全:编译时检查,避免运行时ClassCastException

  2. 代码复用:编写通用的算法和数据结构

  3. 消除强制转换:使代码更简洁清晰

  4. 类型擦除:理解Java泛型的实现机制

10.2 使用原则

  1. 尽量使用泛型:提高代码类型安全性

  2. 合理使用通配符:增加API灵活性

  3. 避免原始类型:失去类型安全检查

  4. 注意类型擦除的影响:运行时无法获取泛型类型信息

  5. 使用有界类型参数:增加类型约束

10.3 适用场景

  1. 集合类:List<T>, Map<K, V>, Set<T>等

  2. 工具类:通用算法、缓存、工厂等

  3. DAO/Repository模式:数据访问层

  4. 回调/事件处理:通用处理器

  5. 框架开发:提供扩展点和插件机制

掌握泛型是Java高级编程的重要基础,能够显著提高代码的质量和可维护性。

相关推荐
吃西瓜的年年2 小时前
5.C语言流程控制语句
c语言·开发语言
码上宝藏2 小时前
从解耦到拓展:Clapper 0.10.0 插件化架构设计与 Lua 脚本集成
linux·开发语言·lua·视频播放器·clapper
qq_117179072 小时前
海康威视球机萤石云不在线问题解决方案
开发语言·智能路由器·php
superman超哥2 小时前
Rust 并发性能调优:线程、异步与无锁的深度优化
开发语言·后端·rust·线程·异步·无锁·rust并发性能
csbysj20202 小时前
Python 多线程
开发语言
superman超哥2 小时前
Rust Trait 对象与动态分发权衡:性能与灵活性的深度权衡
开发语言·后端·rust·rust trait·对象与动态发布·性能与灵活性
ftpeak2 小时前
Burn:纯 Rust 小 AI 引擎的嵌入式物体识别之旅(一步不踩坑)
开发语言·人工智能·rust
独断万古他化2 小时前
【Spring Web MVC 入门实战】实战三部曲由易到难:加法计算器 + 用户登录 + 留言板全流程实现
java·后端·spring·mvc
学后端的小萝卜头2 小时前
如何通过HTTP Range请求分段获取OSS资源(下载篇)
java·网络·http