参数用? super t是为安全消费t类型:因实际类型必为t或其父类,t可安全向上转型;返回值用? extends r是为安全产出r类型:因实际类型必为r或其子类,可被r安全引用,符合pecs原则。

函数式接口中参数用 ? super T(逆变),返回值用 ? extends R(协变),本质是围绕“安全读写”和“Liskov 替换原则”做的类型设计。不是语法炫技,而是让泛型在保持类型安全的前提下,尽可能放宽调用边界。
为什么参数要逆变(? super T)?
逆变让函数能接收更宽泛的输入——只要能“装得下”T就行。比如 Function super Integer, String> 可以传入 Function<number string></number> 或 Function<object string></object>,因为 Integer 能安全地当作 Number 或 Object 使用。
- 实际场景:你写一个
map操作,想把List<integer></integer>映射成字符串。只要函数能处理Integer(或它的任意父类),它就“够用” - 反例:如果写死
Function<integer string></integer>,那传Function<number string></number>就编译失败,哪怕逻辑上完全兼容 - 关键点:参数是“消费者”角色(consumer),越抽象越安全——这是 PECS 原则里的 “C”(Consumer Super)
为什么返回值要协变(? extends R)?
协变让函数可以返回更具体的类型——只要结果“属于”R 就行。比如 Function<string extends number></string> 可以返回 Integer、Double,甚至 AtomicInteger,因为它们都是 Number 的子类。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 实际场景:
Optional<t>.map(Function super T, ? extends U>)</t>中,你传入一个返回BigDecimal的函数,而目标U是Number,完全合法 - 反例:若限定死返回
Number,你就无法用返回Integer的 lambda,哪怕Integer更精确、更常用 - 关键点:返回值是“生产者”角色(producer),越具体越有用——对应 PECS 原则里的 “P”(Producer Extends)
合起来看:Function super T, ? extends R> 的意义
这个签名不是为了绕口令,而是精准刻画函数行为:它消费 T 或其父类(不挑食),产出 R 或其子类(有精度保障)。RxJava 的 map、Stream 的 map、Optional 的 map 全部采用这一模式,正是因为它是对“转换函数”最通用又最安全的抽象。
- 它允许你复用已有函数:一个
Function<number bigdecimal></number>可以用于map任何Integer、Long列表 - 它防止运行时错误:不会让你往
List<integer></integer>里塞String,也不会让你把Object当Integer用 - 它不牺牲表达力:你仍可写
s -> s.length()这样的简洁 lambda,类型系统在背后默默做推导和校验
注意:这不是 Java 的“默认行为”,而是你主动选择的安全契约
Java 泛型默认是“不变”(invariant):List<string></string> 和 List<object></object> 没有继承关系。逆变/协变必须显式用通配符开启。这意味着——
- 你不加
? super或? extends,编译器就按最严格方式检查,容易报错但绝对安全 - 你加了,就要自己理解语义:逆变放宽输入、协变放宽输出,二者共同服务于“一次编写,多处复用”
- 别试图在普通方法参数里硬套逆变(比如
void f(List super String>)),除非你真需要接收List<object></object>并往里 add"abc"
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










