Method reference in Java 8

假装没事ソ 提交于 2021-02-06 15:26:11

问题


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;

回答1:


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




回答2:


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;



回答3:


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.




回答4:


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.




回答5:


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


来源:https://stackoverflow.com/questions/37618069/method-reference-in-java-8

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!