Java泛型Skill java-generics

Java泛型技能用于在Java编程中实现类型安全、可复用的代码,通过泛型类、方法、通配符和类型边界等机制,在编译时进行类型检查,避免运行时错误。关键词:Java, 泛型, 类型安全, 编译时检查, 可复用代码, 泛型方法, 通配符, 类型边界, PECS原则

后端开发 0 次安装 0 次浏览 更新于 3/25/2026

名称: java-generics 用户可调用: 假 描述: 当需要Java泛型包括类型参数、通配符和类型边界时使用。用于编写类型安全可复用的代码。 允许的工具:

  • Bash
  • Read
  • Write
  • Edit

Java泛型

掌握Java的泛型系统,用于编写类型安全、可复用的代码,支持编译时类型检查、泛型类、方法、通配符和类型边界。

泛型介绍

泛型允许在定义类、接口和方法时将类型作为参数,提供编译时类型安全。

基本泛型类:

public class Box<T> {
    private T content;

    public void set(T content) {
        this.content = content;
    }

    public T get() {
        return content;
    }

    public static void main(String[] args) {
        // 字符串的类型安全盒子
        Box<String> stringBox = new Box<>();
        stringBox.set("Hello");
        String value = stringBox.get(); // 无需类型转换

        // 整数的类型安全盒子
        Box<Integer> intBox = new Box<>();
        intBox.set(42);
        Integer number = intBox.get();
    }
}

带多个类型参数的泛型:

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

    public static void main(String[] args) {
        Pair<String, Integer> pair = new Pair<>("age", 30);
        String key = pair.getKey();
        Integer value = pair.getValue();
    }
}

泛型方法

泛型方法可以独立于泛型类定义。

基本泛型方法:

public class GenericMethods {
    // 泛型方法
    public static <T> void printArray(T[] array) {
        for (T element : array) {
            System.out.println(element);
        }
    }

    // 带返回类型的泛型方法
    public static <T> T getFirst(T[] array) {
        if (array.length > 0) {
            return array[0];
        }
        return null;
    }

    public static void main(String[] args) {
        String[] strings = {"a", "b", "c"};
        Integer[] numbers = {1, 2, 3};

        printArray(strings);  // T推断为String
        printArray(numbers);  // T推断为Integer

        String first = getFirst(strings);
        Integer firstNum = getFirst(numbers);
    }
}

带多个类型参数的泛型方法:

public class MultiplTypeParams {
    public static <K, V> Map<K, V> createMap(K key, V value) {
        Map<K, V> map = new HashMap<>();
        map.put(key, value);
        return map;
    }

    public static <T, R> R transform(T input, Function<T, R> transformer) {
        return transformer.apply(input);
    }

    public static void main(String[] args) {
        Map<String, Integer> map = createMap("count", 10);

        String result = transform(42, num -> "Number: " + num);
        // 结果: "Number: 42"
    }
}

有界类型参数

类型边界限制可用作类型参数的类型。

上界类型参数:

public class UpperBound {
    // T必须是Number或其子类
    public static <T extends Number> double sum(List<T> numbers) {
        double total = 0;
        for (T num : numbers) {
            total += num.doubleValue();
        }
        return total;
    }

    // 多个边界
    public static <T extends Comparable<T> & Serializable> T max(T a, T b) {
        return a.compareTo(b) > 0 ? a : b;
    }

    public static void main(String[] args) {
        List<Integer> integers = List.of(1, 2, 3, 4, 5);
        double sum = sum(integers); // 15.0

        List<Double> doubles = List.of(1.5, 2.5, 3.5);
        double doubleSum = sum(doubles); // 7.5

        String maxStr = max("apple", "banana"); // "banana"
    }
}

带边界类型参数的类:

public class NumberBox<T extends Number> {
    private T number;

    public NumberBox(T number) {
        this.number = number;
    }

    public double doubleValue() {
        return number.doubleValue();
    }

    public boolean isZero() {
        return number.doubleValue() == 0.0;
    }

    public static void main(String[] args) {
        NumberBox<Integer> intBox = new NumberBox<>(42);
        NumberBox<Double> doubleBox = new NumberBox<>(3.14);

        // 编译错误: String不是Number
        // NumberBox<String> stringBox = new NumberBox<>("fail");
    }
}

通配符

通配符在处理泛型类型时提供灵活性。

无界通配符:

public class UnboundedWildcard {
    // 接受任何List
    public static void printList(List<?> list) {
        for (Object elem : list) {
            System.out.println(elem);
        }
    }

    public static int size(List<?> list) {
        return list.size();
    }

    public static void main(String[] args) {
        List<String> strings = List.of("a", "b", "c");
        List<Integer> integers = List.of(1, 2, 3);

        printList(strings);
        printList(integers);

        System.out.println(size(strings));  // 3
        System.out.println(size(integers)); // 3
    }
}

上界通配符:

public class UpperBoundedWildcard {
    // 接受Number或其任何子类的List
    public static double sum(List<? extends Number> numbers) {
        double total = 0;
        for (Number num : numbers) {
            total += num.doubleValue();
        }
        return total;
    }

    public static void main(String[] args) {
        List<Integer> integers = List.of(1, 2, 3);
        List<Double> doubles = List.of(1.5, 2.5);
        List<Number> numbers = List.of(1, 2.5, 3);

        System.out.println(sum(integers)); // 6.0
        System.out.println(sum(doubles));  // 4.0
        System.out.println(sum(numbers));  // 6.5
    }
}

下界通配符:

public class LowerBoundedWildcard {
    // 接受Integer或其任何超类的List
    public static void addIntegers(List<? super Integer> list) {
        for (int i = 1; i <= 5; i++) {
            list.add(i);
        }
    }

    public static void main(String[] args) {
        List<Integer> integers = new ArrayList<>();
        addIntegers(integers);
        System.out.println(integers); // [1, 2, 3, 4, 5]

        List<Number> numbers = new ArrayList<>();
        addIntegers(numbers);
        System.out.println(numbers); // [1, 2, 3, 4, 5]

        List<Object> objects = new ArrayList<>();
        addIntegers(objects);
        System.out.println(objects); // [1, 2, 3, 4, 5]
    }
}

PECS原则

生产者扩展,消费者超级 - 使用通配符的指导原则。

PECS在行动中:

public class PECSExample {
    // 生产者 - 从源读取(扩展)
    public static <T> void copy(
        List<? extends T> source,
        List<? super T> destination
    ) {
        for (T item : source) {
            destination.add(item);
        }
    }

    // 生产者 - 扩展用于读取
    public static double sumNumbers(List<? extends Number> numbers) {
        double sum = 0;
        for (Number num : numbers) { // 读取(生成值)
            sum += num.doubleValue();
        }
        return sum;
    }

    // 消费者 - 超级用于写入
    public static void addNumbers(List<? super Integer> list) {
        for (int i = 1; i <= 3; i++) {
            list.add(i); // 写入(消耗值)
        }
    }

    public static void main(String[] args) {
        List<Integer> source = List.of(1, 2, 3);
        List<Number> destination = new ArrayList<>();

        copy(source, destination);
        System.out.println(destination); // [1, 2, 3]
    }
}

泛型接口

接口可以是泛型的,为泛型类型提供契约。

泛型接口:

public interface Repository<T, ID> {
    T findById(ID id);
    List<T> findAll();
    void save(T entity);
    void delete(ID id);
}

public class UserRepository implements Repository<User, Long> {
    private Map<Long, User> storage = new HashMap<>();

    @Override
    public User findById(Long id) {
        return storage.get(id);
    }

    @Override
    public List<User> findAll() {
        return new ArrayList<>(storage.values());
    }

    @Override
    public void save(User user) {
        storage.put(user.getId(), user);
    }

    @Override
    public void delete(Long id) {
        storage.remove(id);
    }
}

class User {
    private Long id;
    private String name;

    public User(Long id, String name) {
        this.id = id;
        this.name = name;
    }

    public Long getId() { return id; }
    public String getName() { return name; }
}

Comparable和Comparator:

public class Person implements Comparable<Person> {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public int compareTo(Person other) {
        return this.name.compareTo(other.name);
    }

    public static void main(String[] args) {
        List<Person> people = new ArrayList<>();
        people.add(new Person("Alice", 30));
        people.add(new Person("Bob", 25));

        // 自然排序(按姓名)
        Collections.sort(people);

        // 自定义比较器(按年龄)
        Comparator<Person> ageComparator =
            Comparator.comparingInt(p -> p.age);
        people.sort(ageComparator);
    }
}

类型擦除

Java泛型使用类型擦除 - 泛型类型信息在运行时被移除。

理解类型擦除:

public class TypeErasure {
    public static void main(String[] args) {
        List<String> strings = new ArrayList<>();
        List<Integer> integers = new ArrayList<>();

        // 在运行时,两者都只是List
        System.out.println(strings.getClass() == integers.getClass());
        // true

        // 不能在运行时检查泛型类型
        // if (list instanceof List<String>) {} // 编译错误

        // 只能检查原始类型
        if (strings instanceof List) {
            System.out.println("Is a List");
        }
    }
}

类型擦除的后果:

public class ErasureConsequences<T> {
    // 不能创建类型参数的实例
    // T instance = new T(); // 编译错误

    // 不能创建参数化类型的数组
    // T[] array = new T[10]; // 编译错误

    // 不能使用instanceof与类型参数
    public boolean isInstance(Object obj) {
        // if (obj instanceof T) {} // 编译错误
        return true;
    }

    // 解决方法: 传递Class<T>
    private Class<T> type;

    public ErasureConsequences(Class<T> type) {
        this.type = type;
    }

    public T createInstance() throws Exception {
        return type.getDeclaredConstructor().newInstance();
    }

    @SuppressWarnings("unchecked")
    public T[] createArray(int size) {
        return (T[]) Array.newInstance(type, size);
    }
}

泛型构建器

使用泛型的构建器模式,用于流畅的API。

泛型构建器:

public class Query<T> {
    private final Class<T> type;
    private String where;
    private String orderBy;
    private int limit;

    private Query(Class<T> type) {
        this.type = type;
    }

    public static <T> Query<T> from(Class<T> type) {
        return new Query<>(type);
    }

    public Query<T> where(String condition) {
        this.where = condition;
        return this;
    }

    public Query<T> orderBy(String field) {
        this.orderBy = field;
        return this;
    }

    public Query<T> limit(int count) {
        this.limit = count;
        return this;
    }

    public List<T> execute() {
        // 执行查询并返回结果
        return new ArrayList<>();
    }

    public static void main(String[] args) {
        List<User> users = Query.from(User.class)
            .where("age > 18")
            .orderBy("name")
            .limit(10)
            .execute();
    }
}

递归类型边界

类型边界可以引用类型参数本身。

带递归边界的枚举:

public class RecursiveBound {
    // 枚举技巧
    public static <E extends Enum<E>> void printEnum(Class<E> enumClass) {
        for (E constant : enumClass.getEnumConstants()) {
            System.out.println(constant);
        }
    }

    // 带递归边界的Comparable
    public static <T extends Comparable<T>> T max(List<T> list) {
        if (list.isEmpty()) {
            throw new IllegalArgumentException("Empty list");
        }

        T max = list.get(0);
        for (T item : list) {
            if (item.compareTo(max) > 0) {
                max = item;
            }
        }
        return max;
    }

    enum Color { RED, GREEN, BLUE }

    public static void main(String[] args) {
        printEnum(Color.class);

        List<String> words = List.of("apple", "banana", "cherry");
        String maxWord = max(words); // "cherry"

        List<Integer> numbers = List.of(1, 5, 3, 9, 2);
        Integer maxNum = max(numbers); // 9
    }
}

带递归边界的构建器:

public abstract class Builder<T, B extends Builder<T, B>> {
    protected abstract B self();

    public abstract T build();
}

public class Person {
    private final String name;
    private final int age;

    protected Person(PersonBuilder<?> builder) {
        this.name = builder.name;
        this.age = builder.age;
    }

    public static PersonBuilder<?> builder() {
        return new PersonBuilder<>();
    }

    public static class PersonBuilder<B extends PersonBuilder<B>>
            extends Builder<Person, B> {
        private String name;
        private int age;

        public B name(String name) {
            this.name = name;
            return self();
        }

        public B age(int age) {
            this.age = age;
            return self();
        }

        @Override
        @SuppressWarnings("unchecked")
        protected B self() {
            return (B) this;
        }

        @Override
        public Person build() {
            return new Person(this);
        }
    }
}

何时使用此技能

在以下情况下使用java-generics:

  • 编写可处理多种类型的可复用代码
  • 强制编译时类型安全
  • 消除运行时类型转换和错误
  • 创建泛型集合、算法或工具
  • 构建类型安全的API和框架
  • 实现通用设计模式
  • 使用Java集合框架
  • 定义带类型参数的灵活方法签名
  • 创建有界类型层次结构
  • 以类型安全方式实现构建器或工厂模式

最佳实践

  • 使用有意义的类型参数名称(如T, E, K, V)
  • 优先使用有界类型参数而非原始类型
  • 在方法参数中使用通配符以增加灵活性
  • 应用PECS原则(生产者扩展,消费者超级)
  • 在新代码中避免使用原始类型
  • 谨慎使用@SuppressWarnings(“unchecked”)
  • 清晰地记录泛型类型约束
  • 尽可能使用泛型方法而非泛型类
  • 使用有界通配符以实现最大API灵活性
  • 考虑类型擦除的影响

常见陷阱

  • 使用原始类型而非参数化类型
  • 混淆扩展和超级通配符
  • 尝试创建泛型类型的数组
  • 不理解类型擦除的限制
  • 过度使用通配符导致代码难以阅读
  • 通配符的方差使用错误
  • 忘记泛型仅在编译时有效
  • 未能正确处理未检查警告
  • 创建不必要复杂的泛型层次结构
  • 错误地在泛型类型中使用instanceof

资源