foreach/filter/map/reduce on Iterable & Iterators

Colin Decker cgdecker at gmail.com
Thu Sep 15 07:46:43 PDT 2011


This doesn't mention Iterables.transform (though I believe the same
applies), but in this mailing list thread [1] Kevin says that the signature
of Lists.transform (which takes a Function<? super F, ? extends T>) was a
mistake and that it should have had the same signature as
Collections2.transform [2] (which takes Function<? super F, T>).

In any case, I don't see it as conceptually the right signature. The map
method differs from a typical method that is using the Mapper as a producer
of U. What you need to think about here is how the resulting Iterable can be
used.

Assigning something that is actually an Iterable<Integer> to
Iterable<Object> or Iterable<Number> doesn't really gain you anything.
You're either going to be using the Iterable yourself, in which case you can
assign its elements to Object or Number directly if you want, or you're
going to be passing it to some other method(s). Assuming those methods use
wildcards correctly, you'll be able to pass the Iterable<Integer> anywhere
you could have passed Iterable<Number> or Iterable<Object>.

Also, you can pass a Mapper<? super MyType, ? extends MyOtherType> to map
even if it takes Mapper<? super T, U>. The result is just going to be an
Iterable<? extends MyOtherType>. That is the actual type of the resulting
Iterable and it's no less usable than if you called it an
Iterable<MyOtherType>. If you're returning that result and don't want the
wildcard in the return type, you can always use Mapper<? super MyType, O
extends MyOtherType> in which case the result is an Iterable<O>.

-- 
Colin

[1] https://groups.google.com/d/msg/guava-discuss/iCW88aubgbc/KuArfyAiIHAJ
[2] http://docs.guava-libraries.googlecode.com/git-history/v9.0/javadoc/com/google/common/collect/Collections2.html#transform(java.util.Collection,
com.google.common.base.Function)


On Thu, Sep 15, 2011 at 8:46 AM, Craig P. Motlin <cmotlin at gmail.com> wrote:

> It seems like it doesn't matter much, but sometimes that flexibility is
> exactly what you need. Guava has the type signature I described [1]. And I
> came to the same conclusion on a similar API. And as Maurizio said, it is
> conceptually the correct type signature. Functions really are covariant in
> their parameter type and contravariant in their return type.
>
> Another way of looking at it - using wildcards this way may be unusual, but
> it should work:
> Mapper<? super MyType, ? extends MyOtherType> mapper = ...;
> myTypes.map(mapper);
>
>  [1]
> http://docs.guava-libraries.googlecode.com/git-history/v9.0/javadoc/com/google/common/collect/Iterables.html#transform(java.lang.Iterable,
> com.google.common.base.Function)
>
>
> On Thu, Sep 15, 2011 at 8:36 AM, Maurizio Cimadamore <
> maurizio.cimadamore at oracle.com> wrote:
>
>> On 15/09/11 12:56, Colin Decker wrote:
>>
>>> "? extends U" isn't necessary for map. All that allows is assigning what
>>> is
>>> really, say, an Iterable<Integer>  to Iterable<Number>  or
>>> Iterable<Object>
>>> without casting, which doesn't help anything.
>>>
>>>  I think they are referring to changing the signature of Collection.map
>> (and other related methods) from this:
>>
>> <U> Iterable<U> map(Mapper<T, U> mapper) ...
>>
>> to this:
>>
>> <U> Iterable<U> map(Mapper<? super T, ? extends U> mapper) ...
>>
>> Now, while this case the '? extends' is conceptually correct, I kinda
>> agree that the outcome won't be too different should the '? extends' be
>> omitted. Since U is a method type-parameter (whose bound is Object -
>> unrelated to T), that means that most of the times type-inference will be
>> able to infer the right type for U; for example:
>>
>> Mapper<Object, Object> moo;
>> Mapper<Number, Number> mnn;
>> Mapper<Integer, Integer> mii;
>>
>> List<Integer> li = ...
>> li.map(moo) // U == Object
>> li.map(mnn) // U == Number
>> li.map(mii) // U == Integer
>>
>> The above will work regardless of whether you put the '? extends' wildcard
>> or not. Where things will be different is when type-parameters are
>> explicitly specified:
>>
>> li.<Object>map(mii) // ok with wildcards / error w/o wildcards (U ==
>> Object)
>> li.<Number>map(mii) // ok with wildcards / error w/o wildcards (U ==
>> Number)
>>
>> Note that the above pattern might be common if i.e. you want to assign the
>> result of the map operation to a type that is less specific than the type of
>> the mapper. I.e.
>>
>> List<Object> lo = li.map(mii) // U == Integer, type-mismatch,
>> List<Integer> not compatible with List<Object>
>> List<Object> lo = li.<Object>map(mii) // ok
>>
>> Maurizio
>>
>>
>>
>>
>>
>>
>>
>


More information about the lambda-dev mailing list