Java中Stream.map签名 Stream map(Function

Java 中 Stream.map 方法的签名是:
<r> Stream<r> map(Function super T, ? extends R> mapper)</r></r>
这个泛型签名不是笔误,而是经过深思熟虑的设计,核心在于支持**逆变输入(? super T)** 和 **协变输出(? extends R)**,从而兼顾类型安全与灵活性。
为什么输入用 ? super T?——提升函数复用性
使用 Function super T, ...> 表示:只要函数能处理 T 或其任意父类型,就可接受。这允许你传入一个“更通用”的函数,而不是严格限定为 Function<t ...></t>。
立即学习“Java免费学习笔记(深入)”;
- 例如:
List<integer> nums = Arrays.asList(1, 2, 3);</integer>,你想转成字符串。你可以复用一个已有的Function<number string></number>(因为Integer是Number的子类),而无需专门为Integer写一个新函数。 - 若写成
Function<t ...></t>,就只能传Function<integer string></integer>,无法复用Function<number string></number>—— 这违背了里氏替换原则和函数式编程中“宽进严出”的习惯。
为什么输出用 ? extends R?——保证返回流元素类型安全
? extends R 表示函数返回值是 R 或其子类型,这样 Stream 能安全地将结果统一视为 R 类型。
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
- 比如
Stream<string> s = stream.map(Object::toString);</string>,Object::toString返回String,而String是Object的子类;但若目标流是Stream<object></object>,这个映射依然合法,因为String可赋值给Object。 - 编译器据此推断:无论函数实际返回
R还是它的子类,都能安全存入Stream<r></r>,不会破坏类型契约。
类型推断如何工作?
Java 编译器会从上下文和 lambda/方法引用中反向推导 T 和 R:
- 输入类型
T来自当前 Stream 的元素类型(如Stream<integer></integer>→T = Integer)。 - 输出类型
R由 lambda 返回值或目标流声明决定(如map(x -> x + 1)在Stream<integer></integer>上 → 推出R = Integer)。 -
? super T和? extends R是“占位符”,不参与最终泛型擦除,只用于编译期检查。
对比手动写法,更能看出设计意图
假设你定义一个“窄类型” map:
<r> Stream<r> map(Function<t r> mapper)</t></r></r>
它看似简洁,但会导致如下问题:
- 无法传入
Function<object string></object>去处理Stream<string></string>(虽然语义上完全合理)。 - 限制了高阶函数组合能力,比如
Function<charsequence integer></charsequence>无法用于Stream<string></string>,尽管String实现了CharSequence。
所以这个签名不是为了炫技,而是让 API 更符合面向对象中的子类型关系和函数式编程的组合需求。

















