compose要求输入用? super t(消费者)、输出用? extends r(生产者),严格遵循pecs原则:前者确保能接收t及父类,后者确保可安全视为r类型。

Observable.compose 的核心作用是将自定义的转换逻辑(即 Transformer)应用到一个 Observable 上,实现链式、可复用的类型安全转换。它本身不改变泛型协变/逆变规则,但其签名对通配符(?)的使用有严格限制——**输入类型必须是上界通配符(? extends T),输出类型必须是下界通配符(? super R)才能满足编译通过**,否则会因类型推导失败而报错。
为什么 compose 对通配符如此敏感?
因为 compose 方法签名是:
<r> Observable<r> compose(Transformer super T, ? extends R> transformer)</r></r>
注意两个关键点:
- 参数
Transformer的输入类型是? super T:表示该 transformer 必须能接受T或其任意父类型(如Object),这样才能安全地接收原始Observable<t></t>的上游数据; - 输出类型是
? extends R:表示 transformer 返回的Observable extends R>可被安全地视为Observable<r></r>,保证下游能消费R类型(或其子类)的元素。
常见错误:误用 ? extends T 作为 transformer 输入
例如写成:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
Observable<string> src = Observable.just("a");
src.compose(new Transformer extends String, Integer>() { ... }); // 编译失败!</string>
错误原因:? extends String 表示“某个未知的 String 子类”,但 Java 中 String 是 final 类,没有子类;更重要的是,Transformer 需要能消费所有 String 实例,而 ? extends String 无法保证这点(它可能对应一个不可实例化的抽象子类型)。编译器拒绝这种写法,因为它破坏了“消费者使用 super”的 PECS 原则(Producer Extends, Consumer Super)。
正确使用通配符的典型场景
当你需要编写通用 transformer 并适配多种上游类型时,应让 transformer 自身声明宽泛的输入边界:
- 若 transformer 仅读取数据(不创建新实例),用
Transformer super Number, String>可同时用于Observable<integer></integer>和Observable<double></double>; - 若 transformer 返回统一类型(如日志包装后的
Result<t></t>),可写为Transformer super T, Result extends T>>,此时输出是Result extends T>,仍满足? extends Result<t></t>的推导要求; - 避免在
compose调用处硬写通配符,而是让类型由上下文自动推导——多数情况下直接传入非通配泛型的 transformer 即可(如Transformer<string integer></string>)。
替代方案:用 lift() 或自定义 operator 更灵活
如果确实需要运行时动态处理不确定类型,compose 不是最佳选择:
-
lift()接收Operator<r t></r>,类型参数明确且不涉及通配符约束,适合底层操作; - 封装为静态方法(如
ObservableUtils.mapToResult())比强行用通配符 transformer 更清晰、易测; - RxJava 2+ 中推荐优先使用
Flowable+compose配合Function,因其泛型更简洁,通配符问题大幅减少。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










