Why Stream.concat is a static method - type variable contravariance

Brian Goetz brian.goetz at oracle.com
Thu Oct 11 16:09:45 UTC 2018

It would surely be convenient; I've wished for this a few times. But, 
there's a reason for this choice, and its not just laziness; 
contravariant type variables have some pretty scary challenges. See, for 
example, this paper:


in which it is shown that contravariance is one of the ingredients in a 
witches brew that leads to undecideability of type checking.

On 10/11/2018 12:06 AM, James Roper wrote:
> With the work I'm doing at the moment at creating a Reactive Streams
> equivalent to java.util.stream, I've often wondered why Stream.concat is a
> static method, rather than an instance method concating the given stream
> onto this. But I think the reason has just dawned on me, and I wanted to
> confirm that I'm correct.
> Java doesn't support contravariant type variables - it does for type
> declarations, but not type variables.
> To put more concretely, if I had a Stream<Integer>, and I wanted to concat
> a Stream<Number>, this is a valid thing to do, the resulting stream would
> be Stream<Number>. But doing that with an instance method would require
> something like this:
> public <S super T> Stream<S> concat(Stream<? extends S> b);
> Which is not supported (specifically, <S super T> type variable declaration
> is not supported). In contrast, what we have in the actual API:
> public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends
> T> b);
> does allow me to concat a Stream<Integer> and Stream<Number> with a
> resulting type of Stream<Number>.
> Is this right, or are there other reasons? Also, is there any possibility
> that Java might support contravariance in type variables in future? My
> reason for wanting it is to provide the following method for reactive
> streams:
> public <S super T> Publisher<S> onErrorResumeWith(Function<? super
> Throwable, ? extends Publisher<? extends S>> f);
> The intent of this method is when a stream encounters an error, the passed
> function is invoked with the error, and that function returns a publisher
> that gets concated to the current stream instead of the error being
> emitted. This could possibly be implemented with a static method:
> public static <T> Publisher<T> onErrorResumeWith(Publisher<? extends T> a,
> Function<? super Throwable, ? extends Publisher<? extends T> f);
> But unlike concat, this method feels and reads much better as an instance
> method, as a static method it's a little confusing.
> Regards,
> James

More information about the core-libs-dev mailing list