public class Car {
private int maxSpeed;
public Car(int maxSpeed) {
this.maxSpeed = maxSpeed;
}
public int getMaxSpeed() {
return maxSpeed;
}
}
We can sort a list of cars by,
Car carX = new Car(155);
Car carY = new Car(140);
List<Car> cars = new ArrayList<>();
cars.add(carX);
cars.add(carY);
cars.sort(Comparator.comparing(Car::getMaxSpeed));
If we see the signature of the method Comparator.comparing, the input parameter type is Function<? super T, ? extends U>
In the above example, how is Car::getMaxSpeed being cast to Function<? super T, ? extends U> while the following does not compile?
Function<Void, Integer> function = Car::getMaxSpeed;
5 Answers 5
That is because the getMaxSpeed method is a Function<Car, Integer>.
Namely:
<Car, Integer> Comparator<Car> java.util.Comparator.comparing(
Function<? super Car, ? extends Integer> keyExtractor
)
Note
In order to reference getMaxSpeed from an instance of Car with the :: idiom, you would have to declare a Car method with signature: getMaxSpeed(Car car).
5 Comments
carX::getMaxSpeed, not Car::getMaxSpeed, i.e. a method already bound to an instance.Function<Car, Integer> ?getMaxSpeed is a member function then to use Car::getMaxSpeed, Car would have to declare a member function getMaxSpeed() (no parameters) or a static function getMaxSpeed(Car)Car::getMaxSpeed is equivalent to car -> car.getMaxSpeed().If you want to create a method reference for a method that takes no parameters, such as a method already bound to an instance, you should use a Supplier, not a Function:
Function<Car, Integer> f1 = Car::getMaxSpeed;
Car carx = new Car(42);
Supplier<Integer> f2 = carx::getMaxSpeed;
In the method reference carX::getMaxSpeed, the "implicit" this-parameter of the function is already bound to carx, so you are left with a no-parameter-function (which, by the way, can not be used in a Comparator), and in Java 8, a no-parameter-function is just a Supplier.
Similarly, if you have a method that returns void, you end up with a Comsumer:
Consumer<Integer> f3 = carx::setMaxSpeed;
1 Comment
Comparator.comparing invocation. edit that's probably not a problem given the context, actually.A member function with no parameters actually has a hidden parameter, the this reference. Method references of the form ClassName::memberFunction always use the first parameter of the functional type for the class instance, i.e. the instance's hidden this parameter. So, in the case of Car.getMaxSpeed(), internally it has the same parameters as a static Integer getMaxSpeed(Car car). Car::getMaxSpeed would therefore fit the functional type Function<Car,Integer>, just as a static Integer getMaxSpeed(Car car) would.
Something similar happens with member functions that take one parameter--they fit the BiFunction functional type, with the first parameter being the class instance.
Comments
The assignment:
Function<Void, Integer> function = carX::getMaxSpeed;
does not compile because it's a Supplier<Integer>, not a Function.
So then, why does this compile?:
Comparator.comparing(Car::getMaxSpeed)
Java 8 allows an instance method reference that is a Supplier<U> to be provided where a Function<T, U> is expected, and the compiler effectively converts the getter method into a function.
To find out why this is possible, let's look at how we invoke a getter method using reflection:
System.out.println(Car.class.getMethod("getMaxSpeed").invoke(carX)); // "155"
When calling invoke() on an instance method, we pass the instance to the invoke() method of the getter's Method - there's an implied parameter of the instance type. When looked at it this way, we see that under the hood a getter is really implemented as a Function<T, U> via the invoke() method.
11 Comments
Comparator.comparing() is expecting a Function. When the supplier method's instance type matches the expected function's parameter type, the supplier method is accepted as a function.Supplier<Integer>, as method references have no type of their own. carX::getMaxSpeed is compatible with a Supplier<Integer> and may be used where a Supplier<Integer> is expected. But it could also be used where a Callable<Integer> or an IntSupplier is expected. These interfaces have the same functional signature, but are otherwise unrelated.Method.invoke has two distinct parameters for the receiver and the arguments, hence, you need pass null as first argument when invoking a static method. Also, Method.invoke is not involved in the process at all. If you want to peek into it, MethodHandles make no distinction between receiver and parameters and are indeed used under the hood to specify the target method.Let's look at Function in detail:
Interface Function<T,R> {
default <V> Function<T,V> andThen(Function<? super R,? extends V> after){}
R apply(T t);
default <V> Function<V,R> compose(Function<? super V,? extends T> before){}
static <T> Function<T,T> identity();
}
note the R apply(T t); Applies this function to the given argument.
Function<Void, Integer> function = Void::?????;
Void voidInstance = null;
function.apply(voidInstance);
This doesn't make sense. You want to pass a Void so that the function of the Void is applied ?
A few illustrative examples of what compiles as a function
note that c->c.getMaxSpeed() and Car::getMaxSpeed are syntactically equivalent if the method is an instanceMethod. For non-static-methods the first argument is infered from the type who's method is used, and needs to be provided later (as the instance which the method will be executed on/applied on).
public class Car {
private int maxSpeed;
public Car(int maxSpeed) {
this.maxSpeed = maxSpeed;
}
public int getMaxSpeed() {
return this.maxSpeed;
}
public Void setMaxSpeed() {
this.maxSpeed = 12;
return null;
}
public static int intStaticFunction(Void v) {
return new Random().nextInt();
}
public static Void voidStaticFunction(Void v) {
return null;
}
public static void main(String[] args) {
final Car carX = new Car(155);
final Car carY = new Car(140);
final List<Car> cars = new ArrayList<>();
cars.add(carX);
cars.add(carY);
cars.sort(Comparator.comparing(Car::getMaxSpeed));
final Function<Car, Integer> function1 = c->c.getMaxSpeed();
final Function<Car, Integer> function2 = Car::getMaxSpeed;
final Function<Car, Void> function3 = Car::setMaxSpeed;
final Function<Void, Void> function4 = n->n;
final Function<Void, Integer> function5 = n->5;
final Function<Void, Integer> function6 = Car::intStaticFunction;
final Function<Void, Void> function7 = Car::voidStaticFunction;
final Function<Car, Integer> function8 = function1::apply;
final Function<Car, Integer> function9 = function2::apply;
System.out.println(function1.apply(carX));
System.out.println(function2.apply(carX));
System.out.println(function8.apply(carX));
System.out.println(function9.apply(carX));
System.out.println(function3.apply(carX));
System.out.println(function1.apply(carX));
System.out.println(function2.apply(carX));
System.out.println(function8.apply(carX));
System.out.println(function9.apply(carX));
System.out.println();
System.out.println(function4.apply(null));
System.out.println(function5.apply(null));
System.out.println(function6.apply(null));
System.out.println(function7.apply(null));
}
}
Comments
Explore related questions
See similar questions with these tags.
Comparator.comparingIntto avoid boxing.carX::getMaxSpeedwhich is very different thanCar::getMaxSpeed. Most answers talk about the first revision, not all. This is confusing: your edit invalidated part of the answers...Void, that is, expecting an instance ofjava.lang.Void, but you are unlikely to ever see something other thannullthere, so it’s not much useful. The point is,Voidis not a wrapper type forvoid, it’s only a placeholder forVoid.TYPEwhich holds theClassobject representingvoid.class.