泛型方法

在一些情况下,定义类、接口时没有使用泛型形参,但定义方法时想自己定义泛型形参,这也可以,Java提供了对泛型方法的支持。

假设需要实现这样一个方法,将Object数组的所有元素都放入Collection集合里,可以考虑代码为

1
2
3
4
static void fromArrayToCollection(Object[] a, Collection<Object> c)
{
...
}

上面方法没有问题,关键在于c形参,它的泛型形参是Object,因为Collection不是Object的子类,所以这个方法功能十分有限,他只能将Object[]数组的元素复制到Collection集合里,但是Object的子类不行。

使用类型通配符也不行,因为Java不允许把对象放进一个未知类型的集合中。

我们可以使用泛型方法,所谓泛型方法,就是在声明方法时,定义一个或多个泛型形参,语法格式为

修饰符 <T, S> 返回值类型 方法名(形参列表){方法体};

泛型方法与普通方法实际上就是多了泛型形参声明,放在修饰符和返回值类型之间,这样就可以把上面方法改为

1
2
3
4
static <T> void fromArrayToCollection(T[] a, Collection<T> c)
{
...
}

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import java.util.*;

public class GenericMethodTest
{
// 声明一个泛型方法,该泛型方法中带一个T泛型形参,
static <T> void fromArrayToCollection(T[] a, Collection<T> c)
{
for (T o : a)
{
c.add(o);
}
}
public static void main(String[] args)
{
var oa = new Object[100];
Collection<Object> co = new ArrayList<>();
// 下面代码中T代表Object类型
fromArrayToCollection(oa, co);
var sa = new String[100];
Collection<String> cs = new ArrayList<>();
// 下面代码中T代表String类型
fromArrayToCollection(sa, cs);
// 下面代码中T代表Object类型
fromArrayToCollection(sa, co);
var ia = new Integer[100];
var fa = new Float[100];
var na = new Number[100];
Collection<Number> cn = new ArrayList<>();
// 下面代码中T代表Number类型
fromArrayToCollection(ia, cn);
// 下面代码中T代表Number类型
fromArrayToCollection(fa, cn);
// 下面代码中T代表Number类型
fromArrayToCollection(na, cn);
// 下面代码中T代表Object类型
fromArrayToCollection(na, co);
// 下面代码中T代表String类型,但na是一个Number数组,
// 因为Number既不是String类型,
// 也不是它的子类,所以出现编译错误
// fromArrayToCollection(na, cs);
}
}

类、接口声明中定义的泛型参数可以在整个类、接口中使用,方法中的泛型参数只能在该方法中使用。

与类、接口中使用泛型参数不同的是,方法中的泛型参数无序显式传入实际类型参数,如上例定义的方法,不需要在调用方法前传入String、Object等类型,系统依然可以知道为泛型实际传入的类型。

## 泛型方法和类型通配符的区别

大多数时候都可用泛型方法来代替类型通配符。

泛型方法允许泛型形参被用来表示方法的一个或多个参数之间的类型依赖关系,或者方法返回值与参数之间的依赖关系,如果没有这样的依赖关系,就不应该使用泛型方法。

当然也可以同时使用泛型方法和通配符,例如

1
2
3
4
5
6
public class Collections {
public static <T> void copy(List<T> dest, List<? extends T> src)
{
...
}
}

上面方法的dest和scr存在明显依赖关系,从scr复制出来的元素可以丢进dest中,这里使用通配符是因为:该方法不需要向src集合添加元素,也不需要修改src集合的元素,所以使用类型通配符,不需要使用泛型方法。

当然,也可以将上面方法改为完全使用泛型方法

1
2
3
class Collections {
public static <T, S extends T> void copy(List<T> dest, List<S> src){};
}

这个可以代替前面的方法,但注意泛型形参S,他仅仅使用了一次,其他参数的类型、方法返回值的类型都不依赖于它,因此S没有存在的必要,可以用通配符来代替S。

类型通配符与泛型方法(在方法签名中显式声明泛型形参)还有一个显著区别:类型通配符既可以在方法签名中定义形参类型,也可以用于定义变量的类型;但是泛型方法中的泛型形参必须在对应方法中显式声明。


泛型方法
https://zhaoquaner.github.io/2022/05/11/Java/泛型/泛型方法/
更新于
2022年5月22日
许可协议