Stream.generate
Remi Forax
forax at univ-mlv.fr
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 univ-mlv.fr> 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;
@Override
public boolean hasNext() {
return true;
}
@Override
@SuppressWarnings("unchecked")
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 :
f.apply((S)element);
} finally {
element = value;
}
}
>
> Paul.
Rémi
More information about the core-libs-dev
mailing list