Skip to content

Java API: accept Iterable<? extends T> for collection parameters #1378

Open

Description

@pjfanning

Several Java API methods take a java.lang.Iterable<T> (or the Scala equivalent java.lang.Iterable[T]) where T has subtypes that Java callers commonly hold in typed variables. Java generics are invariant, so a caller with e.g. a List<RawHeader> cannot pass it to a parameter of type Iterable<HttpHeader> without copying or casting.

Changing these parameters to Iterable<? extends T> (java.lang.Iterable[_ <: T] in Scala) fixes this. The erased signature is unchanged, so this should be binary compatible, and existing Java callers keep compiling.

#1374 does this for Trailer.addHeaders / withHeaders.

Candidates

HttpHeader

  • HttpMessage.MessageTransformations.addHeaders / withHeaders (HttpMessage.java). The scaladsl HttpRequest / HttpResponse overrides need to change to match.
  • RouteDirectives.complete(..., headers, ...) overloads (RouteDirectives.scala)
  • Marshaller.entityToResponse / entityToOKResponse (Marshaller.scala)

Rejection and related

  • RouteResults.rejected (RouteResult.scala)
  • Rejections.unsatisfiableRange: ByteRange has subtypes Slice / FromOffset / Suffix (Rejections.scala)
  • Rejections.unsupportedRequestContentType: MediaType has subtypes Binary / NonBinary (Rejections.scala)

SocketOption

WebSocket handlers: Java sees Flow / Source / Sink / FlowShape as invariant, so a Flow<Message, TextMessage, NotUsed> (what Flow.of(Message.class).map(m -> TextMessage.create(...)) infers) or a Source<TextMessage, ?> cannot be passed. Our own docs and tests work around this with Arrays.<Message>asList(...) and (Message) TextMessage.create(...) casts. The fix is Flow[? >: Message, ? <: Message, T], FlowShape[? >: Message, ? <: Message], SinkShape[? >: Message] and SourceShape[? <: Message].

Smaller items

  • RouteDirectives.completeOKWithFuture(CompletionStage<RequestEntity>): entity.toStrict(...) returns CompletionStage<HttpEntity.Strict>, which cannot be passed. Use CompletionStage<? extends RequestEntity>. completeWithFuture(CompletionStage<HttpResponse>) and completeWithFutureStatus(CompletionStage<StatusCode>) could change too, for consistency (RouteDirectives.scala)
  • JsonEntityStreamingSupport / CsvEntityStreamingSupport.withFramingRendererFlow(Flow<ByteString, ByteString, NotUsed>): the materialized type is fixed to NotUsed, so a flow that materializes anything else is rejected. It should be ? (EntityStreamingSupport.scala)
  • Rejections.unsupportedRequestContentType(..., Optional<ContentType>): ContentType has subtypes (WithCharset, Binary, ...), so use Optional<? extends ContentType> (Rejections.scala)

Out of scope

  • Final or effectively final element types (String, Pair<String, String>, the Coder enum, HttpMethod, javadsl HttpEncoding)
  • Return types / getters, where a wildcard makes the result harder to use
  • Function inputs such as Function<List<String>, Route>, where the value flows to the caller
  • Function / Supplier / BiFunction parameters (e.g. Supplier<Route> inner routes, request handler functions): Java infers lambda and method-reference types from the parameter, so wildcards only help callers holding pre-typed function variables, at the cost of hundreds of signature changes
  • Marshaller / Unmarshaller parameters: they are paired with a method type parameter (complete[T](value: T, m: Marshaller[T, RequestEntity])), so Java inference already finds a fitting type

Each change should come with a Java test that passes a collection of a subtype (e.g. List<RawHeader>), and the MiMa check should still pass.

Suggested PRs

  1. HttpHeader (accept Iterable<? extends HttpHeader> in Java API header parameters #1379): HttpMessage.addHeaders / withHeaders (with the scaladsl HttpRequest / HttpResponse overrides), the RouteDirectives.complete overloads, and Marshaller.entityToResponse / entityToOKResponse. This one matters most, because it is the same API as Trailer in add java.lang.Iterable overloads to Trailer.addHeaders and withHeaders #1374.
  2. Rejections (accept wildcard element types in Java API Rejection factories #1380): RouteResults.rejected, Rejections.unsatisfiableRange and Rejections.unsupportedRequestContentType. accept wildcard element types in Java API Rejection factories #1380 also covers the Optional<ContentType> parameter of unsupportedRequestContentType from "Smaller items".
  3. SocketOption (accept Iterable<? extends SocketOption> in Java API withSocketOptions #1381): withSocketOptions on ClientConnectionSettings and ServerSettings. This is the smallest change and could go into either of the PRs above.
  4. WebSocket handlers (accept Message subtype and supertype handlers in Java WebSocket APIs #1382): all the Message flow / source / sink parameters above, with a Java test that passes a Flow<Message, TextMessage, NotUsed> and a Source<TextMessage, NotUsed>. accept Message subtype and supertype handlers in Java WebSocket APIs #1382 also removes the docs and test workarounds (Arrays.<Message>asList, (Message) casts).
  5. Smaller items (accept wildcard result and materialized types in remaining Java APIs #1384): completeOKWithFuture (and the sibling completeWith*Future* methods) and withFramingRendererFlow. The unsupportedRequestContentType item is in accept wildcard element types in Java API Rejection factories #1380.

Also: #1383 adds tests for the Trailer java.lang.Iterable overloads from #1374 with non-List Iterables.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions