Java 泛型详解

一、什么是泛型

泛型(Generics)是 JDK 5.0 引入的新特性,它允许在定义类、接口或方法时使用类型参数。这种类型参数在使用时指定具体类型。

  1. 泛型是什么?类型的参数

  2. 为什么要用?编译时发现错误,不用运行时崩溃

  3. <T>是什么?占位符,使用时替换成具体类型

  4. <?>是什么?不知道具体类型,只能读不能写

  5. <? extends T>知道是T或其子类,可以安全读取为T

  6. <? super T>知道是T或其父类,可以安全写入T

二、为什么需要泛型

1. 类型安全

java 复制代码
// 没有泛型的问题
List list = new ArrayList();
list.add("hello");
list.add(123);  // 编译通过,运行时可能出错
String s = (String) list.get(1);  // 运行时 ClassCastException
java 复制代码
// 使用泛型
List<String> list = new ArrayList<>();
list.add("hello");
// list.add(123);  // 编译错误
String s = list.get(0);  // 不需要强制类型转换

2. 消除强制类型转换

3. 提高代码复用性

三、泛型的基本使用

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

// 使用泛型类
Box<String> stringBox = new Box<>("Hello");
Box<Integer> integerBox = new Box<>(123);

2. 泛型接口

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

// 实现泛型接口
public class UserRepository implements Repository<User> {
    @Override
    public void save(User entity) {
        // 实现保存逻辑
    }
    
    @Override
    public User findById(Long id) {
        // 实现查询逻辑
        return new User();
    }
}

3. 泛型方法

java 复制代码
public class GenericMethods {
    // 泛型方法
    public <T> void printArray(T[] array) {
        for (T element : array) {
            System.out.println(element);
        }
    }
    
    // 静态泛型方法
    public static <T> T getFirst(T[] array) {
        return array[0];
    }
}

// 使用泛型方法
GenericMethods gm = new GenericMethods();
String[] strings = {"a", "b", "c"};
gm.printArray(strings);

Integer[] numbers = {1, 2, 3};
Integer first = GenericMethods.getFirst(numbers);

四、类型参数命名约定

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

  • K - Key (键)

  • V - Value (值)

  • N - Number (数字)

  • T - Type (类型)

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

五、类型通配符

1. 上界通配符 <? extends T>

java 复制代码
public void processNumbers(List<? extends Number> list) {
    // 可以读取,但不能添加(null除外)
    for (Number n : list) {
        System.out.println(n.doubleValue());
    }
    // list.add(123);  // 编译错误
}

// 可以传入 List<Integer>, List<Double>, List<Number>

2. 下界通配符 <? super T>

java 复制代码
public void addNumbers(List<? super Integer> list) {
    // 可以添加 Integer 或其子类
    list.add(123);
    // list.add(new Object());  // 编译错误
    
    // 读取时需要强制类型转换
    Object obj = list.get(0);
}

// 可以传入 List<Integer>, List<Number>, List<Object>

3. 无界通配符 <?>

java 复制代码
public void printList(List<?> list) {
    // 只能使用 Object 的方法
    for (Object obj : list) {
        System.out.println(obj);
    }
    // list.add("hello");  // 编译错误
}

六、泛型限制

1. 类型擦除

Java 泛型是编译期的特性,运行时类型信息被擦除。

java 复制代码
// 编译后,所有泛型信息都被擦除为原始类型
List<String> list1 = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();

// 运行时 list1.getClass() == list2.getClass() 返回 true

2. 不能实例化泛型类型

java 复制代码
public class Example<T> {
    // 错误示例
    private T instance = new T();  // 编译错误
    
    // 正确做法:使用反射(需传递 Class 对象)
    private Class<T> clazz;
    
    public Example(Class<T> clazz) {
        this.clazz = clazz;
    }
    
    public T createInstance() throws Exception {
        return clazz.newInstance();
    }
}

3. 不能使用基本类型

java 复制代码
// 错误:不能使用基本类型
// List<int> list = new ArrayList<>();  // 编译错误

// 正确:使用包装类
List<Integer> list = new ArrayList<>();

4. 不能创建泛型数组

java 复制代码
// 错误示例
// T[] array = new T[10];  // 编译错误

// 正确做法
public class ArrayExample<T> {
    private T[] array;
    
    @SuppressWarnings("unchecked")
    public ArrayExample(Class<T> clazz, int size) {
        array = (T[]) Array.newInstance(clazz, size);
    }
}

七、实际应用示例

1. 泛型工具类

java 复制代码
public class Pair<K, V> {
    private K key;
    private V value;
    
    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }
    
    // getters and setters
}

2. 泛型与集合框架

java 复制代码
// 安全的集合操作
Map<String, List<Student>> studentMap = new HashMap<>();

List<Student> mathStudents = new ArrayList<>();
mathStudents.add(new Student("张三", 20));
studentMap.put("数学", mathStudents);

// 类型安全的获取
List<Student> students = studentMap.get("数学");

3. 泛型与比较器

java 复制代码
public class ComparatorExample {
    // 通用比较器
    public static <T extends Comparable<T>> T max(T a, T b) {
        return a.compareTo(b) > 0 ? a : b;
    }
}

八、最佳实践

  1. 使用有意义的类型参数名

  2. 尽量使用泛型方法而不是泛型类(当只有少数方法需要泛型时)

  3. 优先使用通配符增加 API 灵活性

  4. 避免使用原生类型

  5. 理解类型擦除的影响

九、Java 8+ 中的增强

1. 目标类型推断改进

java 复制代码
// Java 7 需要指定类型
List<String> list = new ArrayList<String>();

// Java 8+ 可以使用菱形操作符
List<String> list = new ArrayList<>();

2. Lambda 表达式中的泛型

java 复制代码
Function<String, Integer> stringToInt = Integer::parseInt;
Supplier<List<String>> listSupplier = ArrayList::new;

总结

Java 泛型提供了编译时类型安全,减少了运行时错误,提高了代码的可读性和重用性。虽然存在类型擦除等限制,但合理使用泛型可以显著提升代码质量。

相关推荐
rainbow68892 小时前
Java17新特性深度解析
java·开发语言·python
bin91532 小时前
C盘瘦身大作战:程序员的存储空间优化全攻略
c语言·开发语言·c盘清理·c盘清理技巧分享
爬山算法2 小时前
Hibernate(79)如何在ETL流程中使用Hibernate?
java·hibernate·etl
小秋学嵌入式-不读研版2 小时前
智能台灯功能重设计方案
开发语言
Z.风止2 小时前
Go-learning(1)
开发语言·笔记·后端·golang
Ron丶2 小时前
iOS 旧版本 App 下载方法汇总:如何获取历史版本 IPA(2026 仍有效)
windows·经验分享·macos·ios·电脑
rainbow68892 小时前
Java实战:5230台物联网设备时序数据处理方案
java
爬山算法2 小时前
Hibernate(80) 如何在数据迁移中使用Hibernate?
java·oracle·hibernate
子木鑫2 小时前
[SUCTF2019 & GXYCTF2019] 文件上传绕过实战:图片马 + .user.ini / .htaccess 构造 PHP 后门
android·开发语言·安全·php