Java如何声明泛型方法?何时需在返回值类型前加<T>?
你看到的两种写法区别,本质是用到的泛型参数声明位置不一样,作用域完全不同,不属于同一种泛型用法。
不需要在返回值前声明<T>的场景
当方法使用的泛型参数是类级别声明的泛型时,不需要重复声明。
看你给出的第二个示例,类定义本身已经声明了泛型参数<T>:
class MyGenericClass<T> { T obj; // T来自类的泛型声明,方法不需要重复定义 void add(T obj) { this.obj=obj; } T get() { return obj; } }
这里的<T>是绑定在类上的,作用域覆盖整个类的实例成员(成员变量、实例方法)。当你实例化类指定具体类型时,比如MyGenericClass<Integer> m_int = new MyGenericClass<>(),类里所有用到T的位置会自动绑定为Integer,方法直接使用T即可,不需要额外声明。
注意:类级别的泛型参数仅对实例成员生效,静态方法无法使用类上声明的泛型——因为静态方法不需要创建类实例就能调用,没有绑定具体类型的时机,所以静态方法如果要使用泛型,必须自己声明方法级别的泛型参数。
必须在返回值前声明<T>的场景
当方法使用的泛型参数是方法独立声明的泛型时,必须在返回值前显式声明。
你看到的Baeldung示例就属于这种情况:这个方法本身是独立的泛型方法,它所在的类可能没有声明对应的泛型参数,或者方法需要使用和类泛型无关的独立类型。返回值前的<T>作用是告诉编译器:后续方法参数、返回值里出现的T是当前方法自定义的泛型类型,不是外部类定义的,也不是拼写错误。
// <T>是方法自定义的泛型参数,必须写在返回值List<T>前面 public <T> List<T> fromArrayToList(T[] a) { return Arrays.stream(a).collect(Collectors.toList()); }
这种方法级别的泛型作用域仅覆盖当前方法,不需要依赖类的泛型声明,调用时编译器会根据传入的参数自动推断T的具体类型:传入String[]就返回List<String>,传入Integer[]就返回List<Integer>,使用非常灵活。
哪怕方法返回值是void,只要用到了方法自定义的泛型参数,就必须声明<T>,例如:
// 返回值为void,但使用了自定义泛型T,必须声明<T> public <T> void printElement(T element) { System.out.println(element); }
泛型方法签名的标准格式
方法独立声明泛型时,签名的固定格式为:
[访问修饰符] <泛型参数列表> 返回值类型 方法名(参数列表) [抛出异常列表]
判断是否需要加<T>只需要记住一个规则:
- 方法里用到的泛型T如果已经在当前类/接口上声明过(实例方法),不需要额外声明
- 方法里用到的泛型T是方法自己专属的(包括静态方法用泛型、非泛型类里的方法用泛型、方法需要和类泛型独立的类型参数),必须在返回值前的尖括号里声明所有用到的泛型参数
易混淆的特殊情况
如果泛型类里的方法自己声明了和类泛型同名的<T>,方法内的T会覆盖类级别的T,两者没有关联,不要混用:
class MyGenericClass<T> { // 这里的T是类声明的泛型 T obj; // 这里方法自己声明了<T>,和类的T完全独立 public <T> List<T> fromArrayToList(T[] a) { return Arrays.stream(a).collect(Collectors.toList()); } }
例如实例化MyGenericClass<String> strObj = new MyGenericClass<>()时,成员变量obj的类型是String,但调用strObj.fromArrayToList(new Integer[]{1,2,3})是完全合法的——因为方法的T是独立的,此时会被推断为Integer,不受类泛型的约束。
内容的提问来源于stack exchange,提问作者mastercooler6

