Skip to content

泛型定义

泛型三剑客:类、接口、方法

泛型不是一种语法糖,它是一种类型抽象机制。JDK 5 引入泛型后,Java 的类型系统多了一个维度——参数化类型。

简单说:泛型就是让类、接口、方法在定义时,接受一个「类型参数」,使用时才确定具体是什么类型。

泛型类

最常见的形式:

java
// 定义时用 <T> 占位
public class Box<T> {
    private T content;

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

    public T get() {
        return content;
    }
}

// 使用时指定具体类型
Box<String> stringBox = new Box<>();
stringBox.put("苹果");
String fruit = stringBox.get(); // 不需要转型

多类型参数

一个类可以有多个类型参数:

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

Pair<String, Integer> entry = new Pair<>("年龄", 25);

类型参数命名约定

不是关键字,但有行业约定:

java
T - Type(类型)
E - Element(集合元素)
K - Key(键)
V - Value(值)
R - Return(返回值)
S/U/V - 第二//四个类型

泛型接口

接口同样可以泛型化:

java
// 定义泛型接口
public interface Generator<T> {
    T produce();
}

// 实现时指定类型
public class StringGenerator implements Generator<String> {
    @Override
    public String produce() {
        return "Hello";
    }
}

// 或者继续保留泛型
public class GenericGenerator<T> implements Generator<T> {
    private T product;

    public GenericGenerator(T product) {
        this.product = product;
    }

    @Override
    public T produce() {
        return product;
    }
}

泛型方法

方法级别的泛型,独立于类的泛型声明:

java
public class Arrays {
    // 交换任意类型数组的两个元素
    public static <T> void swap(T[] array, int i, int j) {
        T temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }

    // 返回第一个元素
    public static <T> T first(T[] array) {
        if (array == null || array.length == 0) {
            return null;
        }
        return array[0];
    }
}

// 调用
String[] names = {"Alice", "Bob", "Carol"};
Arrays.swap(names, 0, 2); // 自动推断 T = String
String first = Arrays.first(names);

方法泛型的位置

注意 <T> 写在返回值前面,这是固定的语法位置:

java
// 正确
public static <T> T method(T arg) { ... }

// 错误
public static T <T> method(T arg) { ... } // 这样写 T 会被当成注解

类型约束

extends 限制泛型能接受的范围:

java
// T 必须是 Number 或其子类
public class NumberBox<T extends Number> {
    private T value;

    public double toDouble() {
        return value.doubleValue(); // 因为限定了范围,可以安全调用 Number 的方法
    }
}

NumberBox<Integer> ok = new NumberBox<>(); // OK
NumberBox<String> fail = new NumberBox<>(); // 编译错误

多约束(用 & 连接):

java
// T 必须是 Comparable 且 Serializable
public class <T extends Comparable & Serializable> void process(T item) { ... }

Diamond 语法

JDK 7 引入的简化写法,编译器自动推断类型:

java
// 完整写法
Box<String> box = new Box<String>();

// Diamond 语法(JDK 7+)
Box<String> box = new Box<>();

类型推断

编译器能自动推断泛型参数:

java
// 编译器推断 T = String
static <T> T identity(T arg) {
    return arg;
}

String s = identity("hello"); // 不需要显式指定类型
Integer i = identity(123);     // 推断 T = Integer

常见错误

不能 new T()

java
public <T> T create() {
    return new T(); // 编译错误
}

因为类型擦除后,T 在运行时不存在。

解决方案:使用反射或工厂模式。

不能创建泛型数组

java
public <T> T[] createArray() {
    return new T[10]; // 编译错误
}

解决方案Array.newInstance() 或传入 Class 对象。

不能用基本类型

java
List<int> list; // 编译错误
List<Integer> list; // 正确

泛型要求类型都是对象,基本类型需要包装成包装类。

总结

泛型定义有三种形式:

形式关键字示例
泛型类<T>class Box<T>
泛型接口<T>interface Generator<T>
泛型方法<T>static <T> void swap(T[] a, int i, int j)

记住:泛型是一种编译时机制,运行时泛型信息会被擦除

基于 VitePress 构建