Java 8 Optional如此判断空值是否为不良实践?求RxJava2替代方案
Optional in RxJava Singles a Bad Practice? Yes, this approach is generally considered an anti-pattern when working with RxJava. Here's why:
Why This Is a Problem
- Redundant Wrapping: RxJava's reactive types (like
Single,Maybe,Observable) are designed to natively signal presence or absence of values. ASingleguarantees exactly one emission, but wrapping that emission in anOptionaladds unnecessary indirection—you’re using a container to represent absence when RxJava already has built-in tools for this (likeMaybefor 0 or 1 emissions) or can handle nullable values directly (especially in Kotlin with null safety). - Boilerplate & Error Risk: Explicitly checking
isPresent()and callingget()introduces repetitive code, and forgetting the check can lead toNoSuchElementExceptioncrashes. This negates the safety and readability benefits of using reactive streams. - Semantic Misalignment: Reactive streams communicate "no more items" via the
onCompleteevent. UsingOptionalto signal absence inside aSingleemission breaks this contract—readers of your code expect aSingleto emit a valid value, not a wrapper that might be empty.
RxJava2-Based Alternatives
Alternative 1: Simplify with Nullable Types (Kotlin-Friendly)
Since Kotlin supports nullable types, you can unwrap the Optional upfront with a map operation, eliminating the need for manual checks in the zip function:
val yNullableSingle = YSingle.map { opt -> opt.orElse(null) } val d = Single.zip( xSingle, yNullableSingle, BiFunction { x: List<X>, y: Y? -> DataContent(x, y) } ).subscribe({ data -> ... }, { t -> ... })
This is cleaner than your original code and uses Kotlin’s null safety to make the absence of Y explicit without extra wrapping.
Alternative 2: Use Maybe<Y> Instead of Single<Optional<Y>>
If you control the source of YSingle, refactor it to return a Maybe<Y>. A Maybe is purpose-built for 0 or 1 emissions, which perfectly matches your "present or absent" use case:
// Assume YMaybe is a Maybe<Y> instead of Single<Optional<Y>> val ySingleWithDefault = YMaybe.defaultIfEmpty(null) val d = Single.zip( xSingle, ySingleWithDefault, BiFunction { x: List<X>, y: Y? -> DataContent(x, y) } ).subscribe({ data -> ... }, { t -> ... })
defaultIfEmpty(null) converts the Maybe to a Single that emits null when the Maybe completes without an item, aligning with RxJava’s native semantics.
Alternative 3: Handle Absence with Observable Zip
If you want to avoid nullable types entirely, use Observable zip to handle the empty case explicitly:
val xObservable = xSingle.toObservable() val yObservable = YSingle.flatMapObservable { opt -> if (opt.isPresent()) Observable.just(opt.get()) else Observable.empty() } val d = Observable.zip( xObservable, yObservable, BiFunction { x: List<X>, y: Y -> DataContent(x, y) } ) // Provide a default when Y is absent .defaultIfEmpty(DataContent(xSingle.blockingGet(), null)) .singleOrError() .subscribe({ data -> ... }, { t -> ... })
Note: Use blockingGet() cautiously here—only if you’re certain xSingle will emit a value (which it should, as a Single).
内容的提问来源于stack exchange,提问作者MaaAn13

