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)
注意两个关键点:
- 参数
Transformer的输入类型是? super T:表示该 transformer 必须能接受T或其任意父类型(如Object),这样才能安全地接收原始Observable<T>的上游数据; - 输出类型是
? extends R:表示 transformer 返回的Observable<? extends R>可被安全地视为Observable<R>,保证下游能消费R类型(或其子类)的元素。
常见错误:误用 ? extends T 作为 transformer 输入
例如写成:
立即学习“Java免费学习笔记(深入)”;
Observable<String> src = Observable.just("a");
src.compose(new Transformer<? extends String, Integer>() { ... }); // 编译失败!
错误原因:? 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>和Observable<Double>; - 若 transformer 返回统一类型(如日志包装后的
Result<T>),可写为Transformer<? super T, Result<? extends T>>,此时输出是Result<? extends T>,仍满足? extends Result<T>的推导要求; - 避免在
compose调用处硬写通配符,而是让类型由上下文自动推导——多数情况下直接传入非通配泛型的 transformer 即可(如Transformer<String, Integer>)。
替代方案:用 lift() 或自定义 operator 更灵活
如果确实需要运行时动态处理不确定类型,compose 不是最佳选择:
-
lift()接收Operator<R, T>,类型参数明确且不涉及通配符约束,适合底层操作; - 封装为静态方法(如
ObservableUtils.mapToResult())比强行用通配符 transformer 更清晰、易测; - RxJava 2+ 中推荐优先使用
Flowable+compose配合Function,因其泛型更简洁,通配符问题大幅减少。


















