Can BiFunction references be passed to methods expecting a functional interface?
I don't see anything in the linked SO answer that contradicts the article.
Regular rules of the type system apply to functional interface.
If you declare a variable as BiFunction<String,String,Integer> bifunc
, you will not be allowed to pass it to a method that requires Comparator<String>
because BiFunction<String,String,Integer>
is not a subtype of Comparator<String>
.
The fact that functional types follow all the usual rules is what allowed this new functionality to be added with minimal perturbations.
And if you wish to make a Comparator
out of a BiFunction
all you have to do is add ::apply
like so:
BiFunction<String,String,Integer> bifunc = (a,b) ->
Integer.compare(a.length(), b.length());
Arrays.sort(array, bifunc::apply);
The article is correct in the sense, that you cant sort based on object of BiFunction
type, but you always can using Comparator
. But hey, they both can have the same body. For example:
private static void sort(Comparator<String> ls){
Arrays.sort(someArray, ls);
}
Comparator<String> comp = (String f1, String f2) -> Integer.compare(f1.length(), f2.length());
sort(comp);
BiFunction<String, String, Integer> func = (String f1, String f2) -> Integer.compare(f1.length(), f2.length());
sort((String f1, String f2) -> Integer.compare(f1.length(), f2.length())); //line-4
sort(func) // compiler error
Above at line-4, you are able to pass a lambda which is exactly same as func
. But you still cannot pass func
to sort
. Lambdas in java8 are an implementation of some FunctionalInterface. Functional Interfaces get their type based on its reference type. Thats how the same lambda on initialization can be either a BiFunction
or Comparator
.
But once a lambda is constructed and it gets it type, then you cant change it. Hence you cannot pass func
of type BiFunction
to sort which expects Comparator
The article is correct. You can not assign e.g. a BiFunction
to a Comparator
.
With that being said, this great article written by Brian Goetz explains the problem in a nice way.
When the compiler encounters a lambda expression, it first lowers (desugars) the lambda body into a method whose argument list and return type match that of the lambda expression
So, a lambda can be desugared - but what does that mean? Well, basically it means that a new method (may) be created that somehow matches the lamdba.
class A {
public void foo() {
List<String> list = ...
list.forEach( s -> { System.out.println(s); } );
}
}
The code above will be desugared to something like this:
class A {
public void foo() {
List<String> list = ...
list.forEach( [lambda for lambda$1 as Consumer] );
}
static void lambda$1(String s) {
System.out.println(s);
}
}
So, in the case of the BiFunction
and Comparator
. The provided lambda can be assigned to both:
// Assign the lambda to a BiFunction
BiFunction<String, String, Integer> b1 =
(first, second) -> Integer.compare(first.length(), second.length());
// Assign the lambda to a Comparator
Comparator<String> c1 =
(first, second) -> Integer.compare(first.length(), second.length());
// But, once the lambda has been assigned to a type you can not reassign it
BiFunction<String, String, Integer> b2 = c1; // <-- Error
Note that once a lambda has been assigned to a type (BiFunction
or Comparator
) then it can not be reassigned, even if the lambda expression matches.