ambiguous type inference while working with primitives

Paulo Silveira paulo.silveira at caelum.com.br
Wed May 9 21:14:11 PDT 2012


I do have problems with even simpler cases (build 1.8.0-jdk8-b35-20120502):

		List<Object> list = new ArrayList<>();
		list.sort(Comparators.comparing(Object::toString));
		list.sort(Comparators.comparing((o) -> o.toString()));

both examples fail compiling:

error: no suitable method found for comparing(member reference)
		list.sorted(Comparators.comparing(Object::toString));
		                       ^
    method Comparators.<T#1>comparing(DoubleMapper<T#1>) is not applicable
      (cyclic inference - cannot infer target type for given
lambda/method reference expression)
    method Comparators.<T#2>comparing(LongMapper<T#2>) is not applicable
      (cyclic inference - cannot infer target type for given
lambda/method reference expression)
    method Comparators.<T#3>comparing(IntMapper<T#3>) is not applicable
      (cyclic inference - cannot infer target type for given
lambda/method reference expression)
    method Comparators.<T#4,U>comparing(Mapper<T#4,U>) is not applicable
      (cyclic inference - cannot infer target type for given
lambda/method reference expression)
  where T#1,T#2,T#3,T#4,U are type-variables:
    T#1 extends Object declared in method <T#1>comparing(DoubleMapper<T#1>)
    T#2 extends Object declared in method <T#2>comparing(LongMapper<T#2>)
    T#3 extends Object declared in method <T#3>comparing(IntMapper<T#3>)
    T#4 extends Object declared in method <T#4,U>comparing(Mapper<T#4,U>)
    U extends Comparable<? super U> declared in method
<T#4,U>comparing(Mapper<T#4,U>)

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));

Going further, couldn't we have an List.sort overload to directly
receive a Mapper as parameter?

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