Java 泛型详解:从入门到实战

发布于:2025-06-30 ⋅ 阅读:(23) ⋅ 点赞:(0)

🧠 一、什么是泛型?

泛型(Generics)是 Java 5 引入的重要特性之一,它允许在定义类、接口和方法时使用类型参数化。通过泛型,我们可以在编写代码时不指定具体类型,而是在使用时再传入具体的类型。

示例:没有泛型的集合操作

Map map = new HashMap();
map.put("key", "value");
String s = (String) map.get("key"); // 必须强制类型转换

如果有人插入了 Integer 类型的值,运行时会抛出 ClassCastException

使用泛型后:

Map<String, String> map = new HashMap<>();
String s = map.get("key"); // 不需要强制类型转换

泛型帮助我们在编译期就发现类型错误,提高了程序的安全性和可读性


✅ 二、泛型的优势

优势 描述
类型安全 编译器会在编译阶段检查类型是否匹配,避免运行时异常
消除强制类型转换 提高代码可读性和安全性
代码复用 同一套逻辑适配多种数据类型
更好的性能表现 减少不必要的类型检查与转换

🔨 三、泛型的基本用法

1. 定义泛型类

public class Box<T> {
    private T item;

    public void setItem(T item) {
        this.item = item;
    }

    public T getItem() {
        return item;
    }
}

使用方式:

Box<String> stringBox = new Box<>();
stringBox.setItem("Hello");
String content = stringBox.getItem(); // 不需要强转

2. 定义泛型接口

public interface Container<T> {
    void add(T item);
    T get(int index);
}

实现该接口的类也需要使用泛型:

public class ListContainer<T> implements Container<T> {
    private List<T> list = new ArrayList<>();

    public void add(T item) {
        list.add(item);
    }

    public T get(int index) {
        return list.get(index);
    }
}

3. 定义泛型方法

public class Util {
    public static <T> T getFirst(List<T> list) {
        return list.isEmpty() ? null : list.get(0);
    }
}

调用方式:

List<String> names = Arrays.asList("Alice", "Bob");
String first = Util.getFirst(names); // 自动推断为 String 类型

📚 四、泛型命名规范

符号 含义
T Type,通用类型
E Element,集合元素
K Key,键
V Value,值
N Number,数字类型

⚠️ 五、泛型的限制与注意事项

1. 泛型不是协变的

List<Integer> intList = new ArrayList<>();
List<Number> numberList = intList; // ❌ 编译错误!

这是为了防止将 Float 添加进 List<Integer> 中,破坏类型安全。

2. 使用通配符 ?

当不需要关心具体类型时,可以使用通配符:

public void printList(List<?> list) {
    for (Object obj : list) {
        System.out.println(obj);
    }
}

还可以结合上下限使用:

  • <? extends T>:表示 T 或其子类
  • <? super T>:表示 T 或其父类

3. 泛型不能使用基本类型

只能使用包装类型,如 IntegerDouble 等,不能直接使用 intdouble

4. 类型擦除(Type Erasure)

Java 的泛型是通过类型擦除实现的,即在运行时泛型信息会被擦除:

List<String> list1 = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();
System.out.println(list1.getClass() == list2.getClass()); // true

这意味着泛型只存在于编译阶段。


🧪 六、自定义泛型类示例

下面是一个简单的泛型容器类 Lhist<V>

public class Lhist<V> {
    private V[] array;
    private int size;

    public Lhist(int capacity) {
        array = (V[]) new Object[capacity];
    }

    public void add(V value) {
        if (size == array.length)
            throw new IndexOutOfBoundsException();
        array[size++] = value;
    }

    public V get(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException();
        return array[index];
    }

    public int size() {
        return size;
    }
}

使用方式:

Lhist<String> stringList = new Lhist<>(10);
stringList.add("Hello");
System.out.println(stringList.get(0)); // 输出 Hello

📦 七、Java 类库中的泛型应用

Java 标准库从 Java 5 开始全面支持泛型,尤其是集合框架:

  • Collection<E>
  • List<E>
  • Set<E>
  • Map<K,V>
  • Queue<E>

例如:

List<String> names = new ArrayList<>();
names.add("Tom");
String name = names.get(0); // 无需强转

还广泛使用了通配符和边界限定:

public boolean addAll(Collection<? extends E> c);

📘 八、总结

特点 描述
类型安全 在编译期检查类型,避免运行时异常
消除强制类型转换 提高代码可读性和安全性
代码复用 一份逻辑适配多种类型
通配符 支持灵活的类型匹配(?? extends T? super T
类型擦除 运行时无泛型信息,仅保留原始类型

📚 参考资料 / 推荐阅读

  • Oracle 官方文档:Generics
  • 《Effective Java》第 5 条:避免不必要的泛型使用
  • 《Java 编程思想》泛型章节

如果你觉得这篇文章对你有帮助,欢迎点赞 + 收藏 + 关注我,后续将持续更新 Java 技术干货内容!


网站公告

今日签到

点亮在社区的每一天
去签到