Java泛型案例

wen java案例 1

Java泛型实战:从类型安全到优雅代码的完整指南

目录导读

  1. 泛型基础 – 为什么需要泛型?从“类型擦除”说起
  2. 核心案例 – 自定义泛型类、方法、接口的实战代码
  3. 通配符与边界? extends? super 的经典应用场景
  4. 类型擦除的陷阱 – 桥方法、重载与反射中的隐蔽问题
  5. 生产环境最佳实践 – 泛型与集合、函数式接口的协同
  6. 常见问答 – 面试与开发中的高频疑问解析

泛型基础:类型安全与代码复用

Java泛型自JDK 5引入,核心价值在于编译期类型检查消除强制转换,一个最简单的例子:

Java泛型案例

// 不用泛型
List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0); // 手动强转,运行期可能ClassCastException
// 使用泛型
List<String> list = new ArrayList<>();
list.add("hello");
String s = list.get(0); // 编译期保证类型安全

关键原理:Java的泛型是“伪泛型”,运行时通过类型擦除(Erasure)将类型参数替换为Object或边界类型,但编译器会在字节码中插入强转和类型检查指令。


核心实战案例:构建线程安全的泛型栈

案例1:自定义泛型类(Stack)

public class GenericStack<E> {
    private Object[] elements;
    private int size = 0;
    private static final int DEFAULT_CAPACITY = 10;
    public GenericStack() {
        elements = new Object[DEFAULT_CAPACITY]; // 不能直接new E[]
    }
    public void push(E item) {
        ensureCapacity();
        elements[size++] = item;
    }
    @SuppressWarnings("unchecked")
    public E pop() {
        if (size == 0) throw new EmptyStackException();
        E result = (E) elements[--size]; // 擦除后强转
        elements[size] = null; // 避免内存泄漏
        return result;
    }
}

注意:泛型数组创建被禁止(new E[10]),正确做法是创建Object[]后强转。

案例2:泛型方法与类型推断

// 定义泛型方法
public static <T> T getMiddle(T... args) {
    return args[args.length / 2];
}
// 调用时类型推断
String middle = getMiddle("Java", "泛型", "实战"); // 推断为String
Number num = getMiddle(1.2, 3, 4.5); // 推断为Number(共同父类)

案例3:泛型接口实现(实现Comparable)

class Person implements Comparable<Person> {
    private String name;
    private int age;
    @Override
    public int compareTo(Person o) { // 直接使用具体类型,无需强转
        return Integer.compare(this.age, o.age);
    }
}

通配符与边界:处理未知类型

无界通配符

用于只读场景,避免修改集合:

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

上界通配符 ? extends T(协变)

读取安全,写入受限:

// 适合读取Number及其子类
public static double sumOfList(List<? extends Number> list) {
    double sum = 0;
    for (Number n : list) sum += n.doubleValue();
    return sum;
}
// list.add(1) 编译错误,因为无法确定具体子类型

下界通配符 ? super T(逆变)

写入安全,读取受限于Object:

// 适合将Integer写入Number的父类集合
public static void addNumbers(List<? super Integer> list) {
    list.add(1); // 合法
    list.add(2); // 合法
}
// 读取时只能Object obj = list.get(0);

PECS原则:Producer用extends,Consumer用super


类型擦除陷阱与解决方案

陷阱1:桥方法

class Parent { public Object get() { return ""; } }
class Child extends Parent {
    @Override public String get() { return "child"; } // 返回类型协变
}
// 编译器生成桥方法:public Object get() { return this.get(); }

陷阱2:泛型重载冲突

// 编译错误:两个方法擦除后参数相同
void method(List<String> list) {}
void method(List<Integer> list) {}

陷阱3:反射获取泛型参数

// 利用TypeToken获取实际类型
Type type = new TypeToken<List<String>>(){}.getType();
if (type instanceof ParameterizedType) {
    ParameterizedType pt = (ParameterizedType) type;
    System.out.println(pt.getActualTypeArguments()[0]); // class java.lang.String
}

生产环境最佳实践

组合泛型与函数式接口

// 定义泛型函数,提取公共逻辑
public static <T, R> List<R> map(List<T> list, Function<T, R> mapper) {
    List<R> result = new ArrayList<>();
    for (T item : list) result.add(mapper.apply(item));
    return result;
}
// 使用Lambda调用
List<Integer> lengths = map(names, String::length);

泛型与Stream API

Map<String, List<Integer>> groupBy = data.stream()
        .collect(Collectors.groupingBy(Item::getCategory));

有限度的泛型继承

// 泛型类不能继承Throwable,但可以继承具体泛型参数
class StringStack extends GenericStack<String> { }

常见问答(FAQ)

Q1:为什么List<String>不能赋值给List<Object> A:因为泛型不可协变,若允许,则List<Object>中可加入任意对象,破坏类型安全,但List<String>可以赋值给List<? extends Object>

Q2:如何创建泛型数组? A:不能直接new T[],建议使用ArrayList<T>代替,或使用(T[]) new Object[size]并配合@SuppressWarnings

Q3:泛型方法中的类型参数和类中的类型参数冲突怎么办?

// 正确做法:方法级类型参数遮蔽类级
class Example<T> {
    public <T> void print(T value) { } // 两个T不同
}

Q4:什么是“无限定通配符”的实际用途? A:主要用于List<?>Map<?, ?>,适合做只读遍历或清空操作,注意不能添加元素,但可以removeclear,也可作为方法参数接收任意泛型集合。

Q5:泛型在静态上下文中的限制? A:静态方法或静态变量不能使用类上的类型参数,因为静态成员属于类而非实例,如果需要在静态方法中使用泛型,必须定义为泛型方法。


Java泛型不仅是类型安全的工具,更是代码抽象能力的放大器,通过掌握类型擦除机制、通配符边界以及常见陷阱,你可以在保持严谨性的同时,写出高度可复用的优雅代码。编译期解决的问题,绝不留到运行期

抱歉,评论功能暂时关闭!