Java泛型方法上限通配符与类型参数使用常见问题解答
Java泛型常见问题解答
Oracle官方Java泛型教程中给出了以下工具方法实现:
public static double sumOfList(List<? extends Number> list) { double s = 0.0; for (Number n : list) s += n.doubleValue(); return s; }调用示例如下:
List<Integer> li = Arrays.asList(1, 2, 3); System.out.println("sum = " + sumOfList(li));
问题1:为何写法public static <T> double sumOfList(List<T extends Number> list)属于非法语法?
Java泛型语法对边界声明的位置有明确约束:命名类型参数的上界/下界必须在定义类型参数的位置声明,不能在后续使用类型参数的位置追加边界。你在方法开头已经声明了无边界的类型参数<T>,后续在List<T extends Number>中试图给已定义的T添加上界,不符合语法规则。只有通配符?可以在使用时直接跟上边界声明,命名类型参数的边界必须和类型参数定义写在一起。
问题2:写法public static <T extends Number> double sumOfList(List<T> list)和教程中的通配符写法是否等效?若等效,该写法是否代码风格更差?
功能上完全等效。两种写法的约束完全一致:仅允许元素类型为Number子类的List作为入参,方法内部只能将列表元素当作Number类型处理,也都无法向列表中写入元素(符合PECS原则中extends作为生产者的约束)。
风格上该写法确实更差。命名类型参数的作用是在方法的多个位置(参数、返回值、方法内部逻辑)复用同一个泛型约束,如果你整个方法逻辑里不需要复用这个T,额外声明一个无用的命名类型参数属于冗余代码,不如直接用通配符写法简洁清晰。
问题3:调用上述教程中实现的sumOfList方法时,为何System.out.println("sum = " + sumOfList<Integer>(li));属于非法写法?
只有泛型方法才支持调用时显式指定类型实参。教程中的sumOfList方法并不是泛型方法:泛型方法要求在返回值前声明类型参数(比如问题2的写法就属于泛型方法),而教程里的方法只是参数使用了带通配符的泛型类型List<? extends Number>,方法本身没有声明自己的类型参数,自然不支持调用时指定<Integer>这种类型实参。
内容的提问来源于stack exchange,提问作者maliesen
相关产品推荐
相关产品推荐

