Both calls are correct:
Collectors.groupingBy((String s)->s.toLowerCase(),Collectors.counting());
Collectors.groupingBy((String s)->s.toLowerCase(Locale.ENGLISH),Collectors.counting());
Since then, why the following one is wrong:
Collectors.groupingBy(String::toLowerCase,Collectors.counting());
after all String::toLowerCase
can not correspond to the second one... Then why IntelliJ says Reference to 'toLowerCase' is ambiguous, both 'toLowerCase(Locale)' and 'toLowerCase()' match
?
String::toLowerCase
must be unambiguously resolved to (String s)->s.toLowerCase()
or did I miss something?
Of course if I put more context to IntelliJ like:
Collector<String,?,Map<String,Long>> c = Collectors.groupingBy(String::toLowerCase,Collectors.counting());
that is correct, but alas in Java 10 var inference type context it is wrong:
var c = Collectors.groupingBy(String::toLowerCase,Collectors.counting());
I understand that compiler can not infer the input type of counting
. If I write:
Collector<String,?,Long> counter = Collectors.counting();
var c = Collectors.groupingBy(String::toLowerCase,counter);
it it correct. Thus again, why compiler is not able to infer the only acceptable form?
-------EDIT--------
I used IntelliJ/compiler interchangeably just because I used IntelliJ first and error reported was :
Reference to 'toLowerCase' is ambiguous, both 'toLowerCase(Locale)' and 'toLowerCase()' match
Compiler's error was much much more unreadable (but contains more hints on why inference fails), something like:
Demo.java:31: error: incompatible types: cannot infer type-variable(s) T#1,K,A,D,CAP#1,T#2
Collectors.groupingBy(String::toLowerCase,Collectors.counting());
^
(argument mismatch; invalid method reference
incompatible types: Object cannot be converted to Locale)
where T#1,K,A,D,T#2 are type-variables:
T#1 extends Object declared in method <T#1,K,A,D>groupingBy(Function<? super T#1,? extends K>,Collector<? super T#1,A,D>)
K extends Object declared in method <T#1,K,A,D>groupingBy(Function<? super T#1,? extends K>,Collector<? super T#1,A,D>)
A extends Object declared in method <T#1,K,A,D>groupingBy(Function<? super T#1,? extends K>,Collector<? super T#1,A,D>)
D extends Object declared in method <T#1,K,A,D>groupingBy(Function<? super T#1,? extends K>,Collector<? super T#1,A,D>)
T#2 extends Object declared in method <T#2>counting()
where CAP#1 is a fresh type-variable:
CAP#1 extends Object from capture of ?
Local Variable Type Inference is one of the most evident change to language available from Java 10 onwards. It allows to define a variable using var and without specifying the type of it. The compiler infers the type of the variable using the value provided. This type inference is restricted to local variables. Old way of declaring local variable.
In Java 10, using the var reserved word for type inference was forbidden when declaring the parameter list of implicitly typed lambda expressions. Now, we can do things like this: The expressed goal of this feature is very simple: allow var to be used to declare the formal parameters of an implicitly typed lambda expression.
The ambiguities are those issues that are not defined clearly in the Java language specification. The different results produced by different compilers on several example programs support our observations. Here we will be discussing in the following order. Attention reader!
Type inference is used in var keyword in which it detects automatically the datatype of a variable based on the surrounding context. The below examples explain where var is used and also where you can’t use it.
This is compiler "weakness", at least until this JEP is in place.
I have already answered almost the same exact question here. There is also another answer from JDK core developers too.
There is also yet another question that is very close to yours.
What matters is that this is known to cause a problem, at times, but has a trivial solution - use a lambda
, and thus an explicit type, according to the JLS
.
My guess is that the compiler is finding two occurrences of toLowerCase
in the String
class, so it decides to first infer from the second argument, Collectors.counting()
, which is resolved to Object
. This leads the compiler to throw an error because it can't find any toLowerCase()
method accepting an Object
.
If we try to define a method to use it as replacement:
static String toLowerCase(String s) {
return s.toLowerCase();
}
the following would then work:
Collectors.groupingBy(Test::toLowerCase, Collectors.counting()); // compiles ok
But if we introduce another overload, the problem appears again:
static String toLowerCase(String s) {
return s.toLowerCase();
}
static String toLowerCase(String s, Locale locale) {
return s.toLowerCase(locale);
}
Collectors.groupingBy(Test::toLowerCase,Collectors.counting()); // fails again
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With