ambiguous type inference while working with primitives
Brian Goetz
brian.goetz at oracle.com
Thu May 10 08:35:30 PDT 2012
> It does not work even if I try to split the statement:
>
> Comparator<Object> c = Comparators.comparing(Object::toString);
>
> The only way to make it work is to be explicit:
>
> list.sort(Comparators.<Object, String>comparing(Object::toString));
Or to cast the Comparator argument.
> Going further, couldn't we have an List.sort overload to directly
> receive a Mapper as parameter?
This is actually where we started, and we pulled backed to the current
situation after getting some API feedback.
The first question anyone asks when you add
List.sort(Mapper)
is "how do I sort in reverse order?" If the argument is a Comparator,
there is a reverse() method on Comparator to reverse the order:
List.sort(comparing(Foo::getSize).reverse())
But without the indirection of going through Comparator, you have to add
sortReverse (or disappoint those who want to sort in the opposite
order.) This in itself isn't bad, but there is a combinatorial
explosion when you consider all the cases:
argument type x direction x serial/parallel
which yields the following explosion of sort() methods on List:
sort(Mapper)
sort(IntMapper)
sort(LongMapper)
sort(DoubleMapper)
reverseSort(Mapper)
reverseSort(IntMapper)
reverseSort(LongMapper)
reverseSort(DoubleMapper)
parallelSort(Mapper)
parallelSort(IntMapper)
parallelSort(LongMapper)
parallelSort(DoubleMapper)
parallelReverseSort(Mapper)
parallelReverseSort(IntMapper)
parallelReverseSort(LongMapper)
parallelReverseSort(DoubleMapper)
So, in the light of morning, that looks like a false economy. By going
through Comparator, which handles all the overloads of Mapper types,
plus reverse (using a smaller total number of methods), this boils down to:
sort(Comparator)
parallelSort(Comparator)
which seems more manageable. The little extra bit of explicitness to
make a comparator out of a Mapper is not so bad.
> public<U extends Comparable<? super U>> void sort(Mapper<T, U>
> mapper) default {
> Collections.sort(this, new MapperComparator<T, U>(mapper));
> }
>
> This would make possible to invoke a quite short list.sort(Class::getAttribute);
>
>
> --
> Paulo Silveira
> Caelum | Ensino e Inovação
> www.caelum.com.br
>
>
> 2012/5/9 Jim Gish<jim.gish at oracle.com>:
>> On 03/06/2012 06:44 PM, maurizio cimadamore wrote:
>>> Hi
>>> This error occurs because you have two applicable methods, namely:
>>>
>>> *) comparing(IntMapper)
>>>
>>> *) comparing(LongMapper)
>>>
>>> When there are multiple applicable methods, the compiler tries to
>>> disambiguate the call-site using a routine called most-specific. The
>>> goal is to look at the two ambiguous signatures and see if one can be
>>> considered 'more specific' than the other. For instance, if you have two
>>> applicable methods, one accepting an Object, and the other accepting an
>>> Integer, the latter would 'win' as Integer is a subtype of Object.
>>>
>>> What's happening in this case is that the compiler doesn't have enough
>>> information to disambiguate the call-site by just looking at the method
>>> signatures - that's because IntMapper and LongMapper are two unrelated
>>> types, so the compiler doesn't know which method to pick - hence the
>>> ambiguity error.
>>>
>>> We are currently exploring the design space, to see if we can allow some
>>> restricted form of structural subtyping in the most specific algorithm
>>> (this would allow, i.e. to prefer IntMapper to LongMapper because the
>>> return type of the IntMapper's descriptor is more specific).
>>>
>>> Maurizio
>>>
>>> On 06-Mar-12 8:51 PM, Antoras wrote:
>>>> I get compiler errors due to ambiguous Mapper-objects. I have a class
>>>> Person which has a method getAge(). If the return value of this method
>>>> is 'int' the following inline Mapper-object produces an error:
>>>>
>>>> Comparator<Person> c = comparing((Person p1) -> p1.getAge());
>>>>
>>>> If the return type is 'Integer' the above code works.
>>>>
>>>> This line:
>>>>
>>>> Comparator<Person> c = comparing(Person::getAge);
>>>>
>>>> works neither with 'Integer' nor with 'int';
>>>>
>>>> Is this expected behavior or should the type checker recognize that
>>>> IntMapper is the first one to "choose"?
>>>>
>>>>
>>>> The error message:
>>>>
>>>> ListTest.java:203: error: reference to comparing is ambiguous, both
>>>> method<T#1>comparing(LongMapper<T#1>) in Comparators and method
>>>> <T#2>comparing(IntMapper<T#2>) in Comparators match
>>>> Comparator<Person> c = comparing((Person p1) -> p1.getAge());
>>>> ^
>>>> where T#1,T#2 are type-variables:
>>>> T#1 extends Object declared in method<T#1>comparing(LongMapper<T#1>)
>>>> T#2 extends Object declared in method<T#2>comparing(IntMapper<T#2>)
>>>>
>>>
>> I think I have the same problem here:
>>
>> StringJoinerTest.java:46: error: reference to mapReduce is
>> ambiguous, both method mapReduce(LongMapper<? super
>> T>,long,LongBinaryOperator) in Iterable and method
>> mapReduce(DoubleMapper<? super T>,double,DoubleBinaryOperator) in
>> Iterable match
>> int altCompOfStringsLength = lastNames.mapReduce( e ->
>> e.length(), 0, (a, b) -> a+b);
>> ^
>> where T is a type-variable:
>> T extends Object declared in interface Iterable
>> 1 error
>>
>> So, how does one go about disambiguating this, other than breaking out
>> the lambda expressions (and somewhat defeating the point of the
>> compactness value). This, for example, works:
>>
>> int stringsLength = lastNames.map(e -> e.length()).reduce(0,
>> (a, b) -> a+b);
>>
>> Thanks,
>> Jim
>>
>>
>>
>>
>
More information about the lambda-dev
mailing list