RxJava 3 新不同 - 5

159 阅读4分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

API变更

主要版本允许通过添加, 更改或删除所有元素来清理和改进API表面.

函数式接口

RxJava 2.x在io.reactivex.functions下引入了函数式接口自定义集, 因此该库的使用在Java 6 和 Java 8上面可能使用相同的类型. 自定义类型的第二个原因是标准Java 8函数类型不支持任何可检查异常, 这在使用RxJava操作的时候可能导致些许不便.

尽管RxJava 3基于Java 8, 标准Java 8函数式接口的问题现在存在于Android平台的desugaring问题及其不能抛出可检查异常. 由此, 3.x保留了相同的自定义类型, 但@FunctionalInterface注解已经应用在这些类型之上(这是安全的, 且在Android平台是忽略掉的).

@FunctionalInterface
interface Function<@NonNull T, @NonNull R> {
    R apply(T t) throws Throwable;
}

此外, Java 8允许在类型实参上声明注解, 类型实参独立使用, 由此全部函数式接口已经接收了可空性注解.

Wider throws

一个在函数式接口中自定义throws Exception时的小缺陷是一些三方API可能会抛出可检查异常, 这些异常并非Exception的子类, 或者仅仅是抛出Throwable.

然而, 有了3.x, 函数式接口也和其它支持的接口一样被拓宽了, 在签名中声明了throws Throwable.

对于提供给RxJava方法的基于lambda或类实现, 这种扩展应该无关紧要:

source.map(v -> {
    if (v == 0) {
        throw new Exception();
    }
    return v;
});

source.filter(new Predicate<Integer>() {
    @Override
    public boolean test() throws Exception {
        throw new IOException();
    }
});

例如, 没有必要仅仅为了它就把throws Exception修改成throws Throwable.

然而, 如果在外面使用这些函数式接口:

static void Integer callFunction(
        Function<Integer, Integer> function, Integer value) throws Exception {
    return function.apply(value);
}

throws的拓宽将必须被传播:

static void Integer callFunction(
        Function<Integer, Integer> function, Integer value) throws Throwable {
    return function.apply(value);
}

新类型

Supplier

RxJava 2.x已经支持标准的java.util.concurrent.Callable, 它的call方法声明默认有throws Exception. 不幸的是, 当我们的自定义函数式接口拓宽了throws Throwable的时候, 是不可能拓宽Callable的, 因为在Java中, 类的实现不能拓宽throws, 只能收窄或者抛弃.

由此, 3.x引入了io.reactivex.rxjava3.functions.Supplier接口, 定义了可能最宽泛的throws:

interface Supplier<@NonNull R> {
    R get() throws Throwable;
}

⚠️ 关于从IDE运行"组织导入"的说明

因为命名的匹配, IDE倾向于导入java.util.function.Supplier而非RxJava的io.reactivex.rxjava3.functions.Supplier. 此外, IDE倾向于给出非描述性错误, 如"Suppliercan't be converted to Supplier", 忽略了关于包差异的事实.

⚠️ 签名变更

为了遵守对更广泛throws的函数式接口的支持, 许多操作符使用java.util.concurrent.Callable现在改为io.reactivex.rxjava3.functions.Supplier. 如果运算符与lambda一起使用,则只需要重新编译:

Flowable.defer(() -> Flowable.just(Math.random()));

然而, 如果如果使用了显式实现的话:

Flowable.defer(new Callable<Double>() {
    @Override
    public Double call() throws Exception {
        return Math.random();
    }
});

接口类型 (Callable -> Supplier)和方法名 (call -> get)必须调整:

Flowable.defer(new Supplier<Double>() {
    @Override
    public Double get() throws Exception {
        return Math.random();
    }
});

Converters

在2.x, 操作符to()使用了泛化的Function, 允许流在汇编时转化成任意类型. 这种方法的缺点是, 每个基本响应式类型在其方法签名中具有相同的Function接口, 因此不可能在同一类中为不同的响应式类型实现多个转换器. 为了解决这个问题, 2.x中引入了as操作符和XConverter接口, 这些接口是不同的, 可以在同一个类上实现. 由于保证了库的二进制兼容性, 因此无法在2.x中修改as的签名.

从3.x开始, as()方法被移除了, 而且to()方法现在可以和他们对应的XConverter接口工作(在包io.reactivex.rxjava3.core):

  • Flowable.to(Function<Flowable<T>, R>) -> Flowable.to(FlowableConverter<T, R>)
  • Observable.to(Function<Observable<T>, R>) -> Observable.to(ObservableConverter<T, R>)
  • Maybe.to(Function<Flowable<T>, R>) -> Maybe.to(MaybeConverter<T, R>)
  • Single.to(Function<Flowable<T>, R>) -> Maybe.to(SingleConverter<T, R>)
  • Completable.to(Function<Completable, R>) -> Completable.to(CompletableConverter<R>)
  • ParallelFlowable.to(Function<ParallelFlowable<T>, R) -> ParallelFlowable.to(ParallelFlowableConverter<T, R>)

如果使用这些方法的时候用了lambda表达式, 只需要重新编译:

// before
source.to(flowable -> flowable.blockingFirst());

// after
source.to(flowable -> flowable.blockingFirst());

如果实现了Function接口(尤其是以异步的方式), 接口类型, 类型实参和throws语句必须进行调整:

// before
source.to(new Function<Flowable<Integer>, Integer>() {
    @Override
    public Integer apply(Flowable<Integer> t) throws Exception {
        return t.blockingFirst();
    }
});

// after
source.to(new FlowableConverter<Integer, Integer>() {
    @Override
    public Integer apply(Flowable<Integer> t) {
        return t.blockingFirst();
    }
});

引用

RxJava 3 新不同 - 1

RxJava 3 新不同 - 2

RxJava 3 新不同 - 3

RxJava 3 新不同 - 4

RxJava 3 新不同 - 5

RxJava 3 新不同 - 6

RxJava 3 新不同 - 7

RxJava 3 新不同 - 8