Why does the Reactive Streams specification exist, how does Project Reactor relate to it, and what is its relationship to java.util.concurrent.Flow?
answer
- standardize async streams + demand across libraries
- 4 interfaces + rule set + TCK, no operators
- Reactor implements it: Flux (0..N), Mono (0..1)
- java.util.concurrent.Flow = identical interfaces, JDK 9
- different package -> adapter not cast (JdkFlowAdapter)
basics
~10 sThe spec gives asynchronous stream libraries a common, vendor-neutral contract so they interoperate with built-in flow control. Reactor implements it (Flux/Mono are Publishers). Java 9's java.util.concurrent.Flow contains the identical interfaces, mirrored into the JDK.
solid answer
~40 sReactive Streams exists to standardize asynchronous, non-blocking data exchange with backpressure across libraries — before it, RxJava, Akka Streams, and others each had incompatible APIs. It defines four interfaces plus a rigorous rule set (the TCK verifies compliance) so a Publisher from one library composes with a Subscriber from another. Project Reactor is one implementation: Flux (0..N) and Mono (0..1) implement org.reactivestreams.Publisher, and Spring WebFlux depends only on those interfaces, letting controllers return any compliant Publisher. In Java 9, the exact same four interfaces were copied into the JDK as java.util.concurrent.Flow.Publisher/Subscriber/Subscription/Processor — semantically identical, same rules, so the standalone org.reactivestreams types and Flow are trivially bridgeable (JdkFlowAdapter in Reactor). The spec is deliberately minimal — it standardizes the handshake, not the operators.
code
java · 18 linesimport java.util.concurrent.Flow;
import org.reactivestreams.Publisher;
import reactor.adapter.JdkFlowAdapter;
import reactor.core.publisher.Flux;
// Flux is an org.reactivestreams.Publisher:
Publisher<Integer> rsPublisher = Flux.range(1, 3);
// Bridge to the JDK's java.util.concurrent.Flow.Publisher (identical shape,
// different package -> needs an adapter, NOT a cast):
Flow.Publisher<Integer> jdkFlow =
JdkFlowAdapter.publisherToFlowPublisher(rsPublisher);
// ...and back again:
Flux<Integer> backToFlux = JdkFlowAdapter.flowPublisherToFlux(jdkFlow);
// A Spring WebFlux controller only needs the Publisher contract, e.g.:
// @GetMapping("/nums") Flux<Integer> nums() { return Flux.range(1, 3); }go deeper
Knows Reactive Streams is a standard and Reactor implements it.
Knows Flux/Mono are Publishers and the spec has four interfaces plus rules.
Explains the motivation (interop + demand), the TCK, and the JDK Flow mirror with adapters.
Discusses the spec as an integration boundary, minimalism (no operators), and library-agnostic WebFlux design.
**Why the spec exists.** By the mid-2010s several JVM libraries offered asynchronous streams — Netflix's **RxJava**, Lightbend's **Akka Streams**, Pivotal's early **Reactor**, and Ratpack — but they had incompatible types and, critically, inconsistent handling of the **fast-producer/slow-consumer** problem (an unbounded source could overwhelm a consumer and exhaust memory). The **Reactive Streams** initiative (2013–2015, engineers from Netflix, Lightbend, Pivotal, and others) produced a tiny standard: **four interfaces** (`Publisher`, `Subscriber`, `Subscription`, `Processor`) plus a written **rule set** and a **Technology Compatibility Kit (TCK)** that libraries run to prove conformance. The goals: (1) a **vendor-neutral contract** so components from different libraries interoperate, and (2) **flow control built into the protocol** via `Subscription.request(n)` — demand flows upstream so consumers pull only what they can handle. (The detailed *strategies* for handling overflow are a separate topic; the spec just guarantees the demand primitive exists.) **What the spec deliberately is NOT.** It standardizes only the **handshake** — subscribe, the four signals, request/cancel. It does **not** define operators (`map`, `filter`, `flatMap`), schedulers, or higher-level types. Those are each library's value-add. This minimalism is why the interface set is so small. **How Project Reactor relates.** Reactor is Spring's chosen implementation. Its two core types: - **`Flux<T>`** — a Publisher of 0..N elements. - **`Mono<T>`** — a Publisher of 0..1 elements. Both **implement `org.reactivestreams.Publisher<T>`** and honor the full rule set (Reactor passes the TCK). Reactor adds the huge operator library, `Schedulers`, context propagation, `Sinks`, etc., on top of the bare interfaces. **Spring WebFlux** is built against the Reactive Streams `Publisher` interface (and Reactor as the default), which is why a WebFlux controller can return a `Flux`, a `Mono`, or even an RxJava `Flowable` (adapted) and the framework handles all of them uniformly. **Relationship to `java.util.concurrent.Flow` (Java 9+).** When the JDK adopted reactive streams, it copied the **exact same four interfaces** into the `java.util.concurrent.Flow` class as nested interfaces: `Flow.Publisher`, `Flow.Subscriber`, `Flow.Subscription`, `Flow.Processor`. They are **semantically identical** to the `org.reactivestreams` versions — same method signatures, same rules — just a different package, so the JDK could offer reactive interop (e.g., `java.net.http.HttpClient` bodies) without an external dependency. Because they match one-for-one, converting between them is mechanical: Reactor provides `reactor.adapter.JdkFlowAdapter` (`flowPublisherToFlux` / `publisherToFlowPublisher`), and the `org.reactivestreams:reactive-streams-flow-adapters` bridge does the same generically. Application code targeting Spring almost always uses the `org.reactivestreams` types (via Reactor); `Flow` matters mostly for pure-JDK interop. **When to care in practice.** You lean on this knowledge when integrating a non-Reactor reactive library into WebFlux, when explaining why WebFlux is 'reactive library agnostic', or when bridging to JDK APIs (`HttpClient`, `SubmissionPublisher`). Day-to-day you just use `Flux`/`Mono`. **Gotchas / misconceptions:** - 'Reactive Streams' the spec is **not** the same as 'Spring Reactor' — Reactor is one implementation of the spec. - The spec does **not** include operators; if someone says 'Reactive Streams has map/flatMap', that's Reactor/RxJava, not the spec. - `Flow` and `org.reactivestreams` are **not** subtypes of each other — identical shape, different packages, requiring an adapter (not a cast).
- If Flow and org.reactivestreams interfaces are identical, why isn't a Flux automatically a Flow.Publisher?Because Java's type system is nominal, not structural: identical method shapes in different packages are unrelated types, so you can't cast between them. You bridge with an adapter (Reactor's JdkFlowAdapter or the reactive-streams-flow-adapters library) that wraps one interface as the other.
- Does the Reactive Streams spec define operators like map and flatMap?No. The spec standardizes only the subscribe handshake and the four signal / demand methods. Operators, schedulers, and higher-level types are provided by implementations like Reactor and RxJava — that's their differentiation on top of the common contract.
- Why can a WebFlux controller return an RxJava Flowable as well as a Reactor Flux?Because WebFlux is coded against the org.reactivestreams.Publisher interface, and both Flux and Flowable implement it. Spring adapts any compliant Publisher, making WebFlux reactive-library-agnostic at the return-type boundary.
saying these in an interview costs you the question
- Equating 'Reactive Streams spec' with 'Project Reactor'
- Claiming the spec includes map/flatMap/schedulers
- Saying Flux is directly a java.util.concurrent.Flow.Publisher without an adapter
- Thinking WebFlux is hard-wired to Reactor with no interop