Java 泛型方法

更新于 2025-12-28

Jakob Jenkov 2024-07-06

Java 泛型不仅可以用于类,也可以用于方法。换句话说,你可以对 Java 方法进行泛型化。更具体地说,你可以在 Java 方法的参数和返回类型中指定泛型类型。

以下是一个 Java 泛型方法的示例:

public static <T> T addAndReturn(T element, Collection<T> collection){
    collection.add(element);
    return element;
}

这个 Java 方法声明了一个泛型类型 T,它既用作 element 参数的类型,也用作 Collection 的泛型类型。注意,现在可以向集合中添加元素了。如果在 Collection 参数定义中使用 Java 泛型通配符(wildcard),这是不可能做到的。

那么,Java 编译器是如何知道 T 的类型的呢?

答案是:Java 编译器会根据你对该方法的调用来推断出类型。

例如:

String stringElement = "stringElement";
List<String> stringList = new ArrayList<String>();
String theElement = addAndReturn(stringElement, stringList);

在这个例子中,Java 编译器会推断出类型 TString,因为传给 addAndReturn() 方法的第一个参数的类型是 String,同时传入的集合的泛型类型也是 String

我们也可以在其他类型上直接使用 addAndReturn(),无需做任何修改。

下面的例子展示了如何将 addAndReturn() 方法与一个 Integer 对象以及泛型类型为 IntegerList 一起使用:

Integer integerElement = new Integer(123);
List<Integer> integerList = new ArrayList<Integer>();
Integer theElement = addAndReturn(integerElement, integerList);

和前面的例子一样,Java 编译器会推断出 addAndReturn() 的泛型类型为 Integer,因为传入的第一个参数的类型是 Integer,而且作为第二个参数传入的 List 的泛型类型也是 Integer

如你所见,完全相同的方法可以以类型安全的方式用于不同的类型!Java 编译器会根据你在代码中如何使用该方法(包括返回类型和参数类型)来推断出该方法应处理的具体类型。

高级类型推断

Java 编译器甚至可以执行更高级的类型推断。例如,下面的调用也是合法的:

String stringElement = "stringElement";
List<Object> objectList = new ArrayList<Object>();
Object theElement = addAndReturn(stringElement, objectList);

在这个例子中,我们对 T 使用了两种不同的类型:StringObject。编译器会选择使方法调用类型正确的最具体的公共类型

注意,使方法调用类型正确的“最具体类型”并不一定是最具体的使用类型。在上面的例子中,最具体的使用类型是 String,但如果编译器将 T 推断为 String,那么将 List<Object> 作为第二个参数传递给 addAndReturn() 就会导致编译错误。因此,编译器将 T 推断为 Object,因为 Object 是在此情况下对两个参数都有效的最具体类型。

然而,反过来的泛型类型组合是不合法的:

Object objectElement = new Object();
List<String> stringList = new ArrayList<String>();
Object theElement = addAndReturn(objectElement, stringList);

在这种情况下,Java 编译器推断出:为了使方法调用类型安全,泛型类型 T 必须是 String。那么传入 T element 参数的 objectElement 也必须是 String 类型(但它不是)。因此,编译器会报错。

将 Class 对象作为类型字面量

假设你有一个方法接收一个 Class 对象,并且需要返回该类的一个实例。这时,你希望 Java 编译器能根据传入的 Class 对象推断出方法的返回类型。这在 Java 中实际上是可行的。

请看下面的例子:

public static <T> T getInstance(Class<T> theClass) throws IllegalAccessException, InstantiationException {
    return theClass.newInstance();
}

注意,该方法在 Class 参数和方法返回类型上都声明了泛型类型 T。这向 Java 编译器表明:方法的返回类型应该与传入的 Class 对象的类型一致。因此,如果你传入 String.class,那么 T 就会被推断为 String

我在教程《Java 泛型 - 将 Class 对象作为类型字面量》中对此有更详细的解释。