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 对象。K 和 V 是占位符,代表 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);
在这段代码中,我们显式地指定了泛型方法的类型参数 Integer 和 String,通过 Util.<Integer, String>compare(p1, p2) 来调用 compare 方法。
类型推理
然而,Java 编译器会根据上下文推断出泛型类型,因此通常不需要显式地提供类型参数。代码可以简化为:
boolean same = Util.compare(p1, p2);
在这里,编译器通过 p1 和 p2 的类型推断出 compare 方法所需的泛型类型参数为 Integer 和 String,因此我们无需显式指定类型参数。这种功能被称为 类型推理(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. 总结
- 泛型方法 允许在方法级别引入类型参数,使方法更加通用。
- 类型推理 使得您可以在调用泛型方法时省略显式指定类型参数。
- 静态和非静态泛型方法 分别适用于不依赖于实例的类方法和依赖于实例的实例方法。
- 使用泛型方法可以提高代码的复用性和可读性,减少冗余代码。
通过泛型方法,您可以编写更加灵活、通用的代码,处理不同类型的对象,而不牺牲类型安全性。