Remi Forax forax at
Wed Jul 22 11:22:14 UTC 2015

On 07/22/2015 10:10 AM, Paul Sandoz wrote:
> On 20 Jul 2015, at 19:08, Remi Forax <forax at> wrote:
>> Hi all, hi Paul,
>> I've found that the signature of Stream.generate doesn't to use a wildcard hence some program are rejected even if there are valid, by example:
>>   public static void main(String[] args) {
>>       Supplier<String> supplier = () -> "hello";
>>       Stream<CharSequence> s = Stream.generate(supplier);
>>   }
>> The fix is simple, in interface Stream, generate should be declared like this :
>>   public static<T> Stream<T> generate(Supplier<? extends T> s)
>> and
>> the field 's' of the InfiniteSupplyingSpliterator.OfRef should be also declared as a Supplier<? extends T>.
> I believe such changes to Stream.generate are backwards compatible because overriding is not possible. (In addition static method on an interface are scoped to that interface, but i am not sure if that matters in this case.)

The fact that a static method on a class is accessible from a subclass 
doesn't help my students to understand how overriding works and i'm glad 
that Kevin Bourillion suggests that for static methods in interface we 
can fix that.

I believe that if Stream was a class, it might cause an issue because i 
remember that the JLS defines name-clash even on static methods i.e. a 
code like this is illegal:

class A {
   static void m(Supplier<Object> s) { ... }
class B extends A {
   static void m(Supplier<? extends Object> s) { ... }  // name clash

> We could also adjust Stream.iterate as well:
>    public static<T, S extends T> Stream<T> iterate(final S seed, final UnaryOperator<S> f)
> Seems appropriate for consistency with Stream.generate. I presume that is backwards compatible as well?

yes !
as you said, these changes are source backward compatible, the static 
methods now accept more potential types and binary backward compatible 
because the erasures are not changed.

BTW, in the body of iterate(), the cast of Stream.NONE to T is plainly 
wrong from the type system point of view, it only works because of 
erasure. A slightly less wrong code is to store the current element in 
an Object and do the cast from Object to T (or S with the new signature) 
when calling f because the parameter is always a T at that point.

          final Iterator<T> iterator = new Iterator<T>() {
             private Object element = Streams.NONE;

             public boolean hasNext() {
                 return true;

             public T next() {
                 Object e = element;
                 S value = (e == Streams.NONE) ? seed : f.apply((S)e);
                 element = value;
                 return value;

The code of next() is less readable because in Java when you write a = b 
= c, the type of b = c is the type of b instead of being the type of c 
(like in Kotlin or Groovy).
A fun hacky solution to simplify the code, and make it obviously not 
readable, is to use a try/finally here :)

             public T next() {
                 T value;
                 try {
                     return value = (element == Streams.NONE) ? seed : 
                 } finally {
                     element = value;

> Paul.


More information about the core-libs-dev mailing list