掘金 后端 ( ) • 2024-04-21 17:54

Java泛型(Generics)是Java SE 5引入的一项核心语言功能,是一种用于在编译时对类型进行检查的技术,它允许程序员在编写接口方法时使用类型参数(type parameters),而不是硬编码具体的类型。

这样做可以让代码更加灵活和可复用,同时增强了类型安全性,能够在编译阶段捕获类型不匹配的错误,减少运行时错误。

基础概念

  • 类型参数:在定义泛型类或方法时,可以使用尖括号 < > 中的类型参数表示待定的类型。
    • 例如,List<T> 中的 T 就是一个类型参数,当你创建一个 List 实例时,可以指定 T 为任何有效类型,如 List<String>List<Integer>
  • 类型约束:类型参数可以带有约束条件,表明它必须是某种特定类型或者其子类型。
    • 例如,T extends Comparable<T> 表示 T 必须是实现了 Comparable<T> 接口的类型。
  • 泛型方法:不只是类可以使用泛型,方法也可以拥有自己的类型参数,使方法独立于类的泛型类型。

优势

  • 类型安全:通过在编译时就进行类型检查,可以防止插入错误类型的元素,从而降低运行时出现 ClassCastException 的风险。
  • 代码复用:通过泛型,可以编写一个通用的类或方法,适用于多种数据类型,避免为每种类型都编写重复的代码。

定义格式

泛型的定义格式主要包括在类、接口、方法和数组中定义泛型类型参数。

定义泛型类

  • 修饰符 class 类名<类型> { }:泛型类通过在其名称后面使用尖括号 < > 内指定类型参数来定义。例如,定义一个名为 Box 的泛型类,其中的 T 表示任意类型:
// 定义一个泛型类
public class Box<T> {
    private T item;

    public Box(T item) {
        this.item = item;
    }

    public T getItem() {
        return item;
    }

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

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

定义泛型接口

  • 修饰符 interface 接口名<类型> { }:泛型接口的定义方式与泛型类类似,同样在接口名称后使用尖括号 < > 指定类型参数:
// 定义一个泛型接口
public interface Generator<T> {
    T generate();
}

// 实现泛型接口
public class StringGenerator implements Generator<String> {
    @Override
    public String generate() {
        return "Generated String";
    }
}

定义泛型方法

  • 修饰符 <类型> 返回值类型 方法名(类型 变量名) { }:泛型方法是在方法签名中声明类型参数,不受类级别的泛型参数影响:
public class Util {
    public static <T> void printArray(T[] array) {
        for (T item : array) {
            System.out.println(item);
        }
    }
}

// 使用泛型方法
Integer[] integers = {1, 2, 3};
Util.printArray(integers);

String[] strings = {"Hello", "World"};
Util.printArray(strings);

泛型类型的边界(类型约束)

  • 可以通过 extendssuper 关键字来指定类型参数的边界,限制它可以是某种类型或其子类/父类:
public class ComparableBox<T extends Comparable<T>> {
    private T item;

    // ... methods ...

    public int compare(T other) {
        return item.compareTo(other);
    }
}

// 使用带边界约束的泛型类
ComparableBox<String> comparableStringBox = new ComparableBox<>("abc");

通配符类型

  • <?>:通配符类型用于更灵活的类型匹配,主要有两种形式:
    • ? 表示未知类型;
    • ? extends SomeType 表示某种类型的子类型;
    • ? super SomeType 表示某种类型的父类型;
public void processElements(List<?> list) {
    for (Object obj : list) {
        System.out.println(obj);
    }
}

// 使用通配符类型的泛型方法
List<String> stringList = new ArrayList<>();
processElements(stringList);
List<Integer> integerList = new ArrayList<>();
processElements(integerList);

注意事项

  • 泛型只能用于引用类型,不能用于基本数据类型。例如,不能有List<int>,而应该使用List<Integer>
  • 泛型类或接口不能有泛型类型的实例字段,因为这样的字段在运行时无法确定具体类型。
  • 泛型的类型擦除:Java泛型在编译时提供类型检查,在运行时不保留泛型类型信息。这意味着泛型类型信息在运行时会被擦除。