Hi Paul, The use of BaseStream was just an example, here is another one that works only if the function first parameter type is declared as '? super Stream<StackWalker.StackFrame>'.
static Function<Stream<?>, Integer> counter() { return stream::count; } ... StackWalker walker = ... int count = walker.walk(counter()); regards, Rémi ----- Mail original ----- > De: "Paul Sandoz" <paul.san...@oracle.com> > Cc: core-libs-dev@openjdk.java.net > Envoyé: Lundi 2 Novembre 2015 13:44:24 > Objet: Re: Proposed API for JEP 259: Stack-Walking API > > I agree with Maurizio, the first signature is good enough. > > One could argue that it might be better to apply PECS since it would > encourage more consistent usage when it is actually required as all too > often it’s easy to forget, and then too late to change. However, i don’t > want to encourage the use of BaseStream since it was an unfortunate mistake > that this made public. > > Paul. > > > On 2 Nov 2015, at 13:26, Maurizio Cimadamore > > <maurizio.cimadam...@oracle.com> wrote: > > > > So, we have three potential signatures here: > > > > <T> T walk(Function<Stream<StackWalker.StackFrame>, T> function) //1 > > > > <T> T walk(Function<Stream<StackWalker.StackFrame>, ? extends T> function) > > //2 > > > > <R extends T, T> T walk(Function<Stream<StackWalker.StackFrame>, R> > > function) //3 > > > > > > Under normal conditions (i.e. lambda parameter being passed to 'walk') I > > think all these signatures are fundamentally equivalent; (2) and (3) seem > > to have been designed for something like this: > > > > Number n = walk(s -> new Integer(1)); > > > > That is, the function returns something that is more specific w.r.t. what > > is expected in the return type of walk. But - in reality, if 'walk' > > returns an R that is a subtype of T (as in the third signature), then walk > > also returns a T (as R is a subtype of T), so the result value can be > > passed where a T is expected. > > > > The converse example: > > > > Integer n1 = walk(s -> (Number)null); > > > > Similarly fails on all three signatures. > > > > > > More generally, with all such signatures, T will always receive: > > > > * lower bound(s) (from the return value(s) of the lambda passed as a > > parameter to the 'walk' method) > > * one upper bound (from the target-type associated with the 'walk' call. > > > > Under such conditions, the upper bound will always be disregarded in favor > > of the lower bounds - meaning that instantiation of T will always be > > driven by what's inside the lambda. Signature (3) mentions different > > variables (R and T) but the end result is the same - as the bound says R > > extends T - meaning that lower bounds of R are propagated to T - leading > > to exactly the same situation. > > > > > > In other words, I don't think there are obvious examples in which the > > behavior of these three signatures will be significantly different - if > > the return type of 'walk' would have been a generic type such as List<T>, > > the situation would have been completely different - with with a 'naked' > > T, I think the first signature is good enough, and the third is, in my > > opinion, making things harder than what they need to be. > > > > I think the second signature is not necessary, from a pure type-system > > perspective; but I guess one could make an argument for it, but purely in > > terms of consistency with other areas (after all, the second > > type-parameter of a Function is in a covariant position). > > > > I hope this helps. > > > > Maurizio > > >