123. Java 泛型 - 泛型方法

156 阅读4分钟

123. Java 泛型 - 泛型方法

1. 什么是泛型方法?

泛型方法是方法中引入自己的类型参数,允许在方法的参数或返回类型中使用类型变量。与泛型类不同,泛型方法的类型参数只在声明该方法的范围内有效,意味着它不会影响类的其他部分。

泛型方法的语法

泛型方法的声明与普通方法略有不同,它包括一个类型参数列表,通常位于方法的返回类型之前,尖括号内列出类型参数。例如:

public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2) {
    return p1.getKey().equals(p2.getKey()) && p1.getValue().equals(p2.getValue());
}

在这个例子中,<K, V> 是方法的类型参数,表示该方法可以处理任何类型的 Pair 对象。KV 是占位符,代表 Pair 中的键和值类型。

泛型方法的示例

考虑以下 Pair 类,用于存储键值对:

public class Pair<K, V> {

    private K key;
    private V value;

    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }

    public void setKey(K key) { this.key = key; }
    public void setValue(V value) { this.value = value; }
    public K getKey()   { return key; }
    public V getValue() { return value; }
}

Pair 是一个泛型类,用来存储键 K 和值 V,并为这些数据提供访问方法。

现在我们有一个名为 Util 的类,其中定义了一个泛型方法 compare,用于比较两个 Pair 对象:

public class Util {
    public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2) {
        return p1.getKey().equals(p2.getKey()) &&
               p1.getValue().equals(p2.getValue());
    }
}

调用泛型方法

我们可以通过如下方式调用 compare 方法:

Pair<Integer, String> p1 = new Pair<>(1, "apple");
Pair<Integer, String> p2 = new Pair<>(2, "pear");

// 显式指定类型
boolean same = Util.<Integer, String>compare(p1, p2);

在这段代码中,我们显式地指定了泛型方法的类型参数 IntegerString,通过 Util.<Integer, String>compare(p1, p2) 来调用 compare 方法。

类型推理

然而,Java 编译器会根据上下文推断出泛型类型,因此通常不需要显式地提供类型参数。代码可以简化为:

boolean same = Util.compare(p1, p2);

在这里,编译器通过 p1p2 的类型推断出 compare 方法所需的泛型类型参数为 IntegerString,因此我们无需显式指定类型参数。这种功能被称为 类型推理(Type Inference)

泛型方法的使用场景

泛型方法非常适合用来处理不同类型的参数,尤其是在您希望编写通用的工具类或方法时。例如,compare 方法可以用于任何类型的 Pair,而不需要为每种类型写一个单独的比较方法。

例子:交换值的泛型方法

假设我们有一个方法,用于交换数组中的两个元素:

public static <T> void swap(T[] array, int i, int j) {
    T temp = array[i];
    array[i] = array[j];
    array[j] = temp;
}

这里的 <T> 是泛型类型,表示 swap 方法可以处理任何类型的数组。我们可以使用这个方法来交换 Integer 数组、String 数组,甚至是自定义对象数组中的元素。

Integer[] nums = {1, 2, 3};
swap(nums, 0, 2);  // 交换 nums[0] 和 nums[2]

String[] words = {"apple", "banana"};
swap(words, 0, 1);  // 交换 words[0] 和 words[1]

这使得 swap 方法非常通用,能够处理任何类型的数组。

2. 静态与非静态泛型方法

静态泛型方法

静态方法是类级别的方法,它们不依赖于对象实例。对于静态泛型方法,类型参数必须放在方法返回类型之前。例如:

public static <T> void printArray(T[] array) {
    for (T element : array) {
        System.out.println(element);
    }
}

调用时同样不需要实例化类对象:

Integer[] nums = {1, 2, 3};
printArray(nums);  // 输出:1 2 3

String[] words = {"apple", "banana"};
printArray(words);  // 输出:apple banana

非静态泛型方法

非静态泛型方法依赖于类的实例,因此它们可以访问类中的实例变量。以下是一个非静态泛型方法的示例:

public class Box<T> {
    private T value;

    public <U> void setValue(U value) {
        System.out.println("Setting value: " + value);
    }
}

这里,setValue 是一个非静态的泛型方法,它可以接受任何类型的参数 U,并输出值。

Box<Integer> box = new Box<>();
box.setValue("Hello");  // 输出:Setting value: Hello

3. 总结

  • 泛型方法 允许在方法级别引入类型参数,使方法更加通用。
  • 类型推理 使得您可以在调用泛型方法时省略显式指定类型参数。
  • 静态和非静态泛型方法 分别适用于不依赖于实例的类方法和依赖于实例的实例方法。
  • 使用泛型方法可以提高代码的复用性和可读性,减少冗余代码。

通过泛型方法,您可以编写更加灵活、通用的代码,处理不同类型的对象,而不牺牲类型安全性。