Ссылка на метод в Java 8

public class Car {

    private int maxSpeed;

    public Car(int maxSpeed) {
        this.maxSpeed = maxSpeed;
    }

    public int getMaxSpeed() {
        return maxSpeed;
    }
}

мы можем сортировать список автомобилей по,

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

если мы видим подпись метода Comparator.comparing входной параметр типа Function<? super T, ? extends U>

в приведенном выше примере, как Car::getMaxSpeed быть брошенным в Function<? super T, ? extends U> пока следующее не компилируется?

  Function<Void, Integer> function = Car::getMaxSpeed;

5 ответов


вот ведь getMaxSpeed метод Function<Car, Integer>.

а именно:

<Car, Integer> Comparator<Car> java.util.Comparator.comparing(
    Function<? super Car, ? extends Integer> keyExtractor
)

Примечание

для справки getMaxSpeed из экземпляра Car С :: идиома, вам придется объявить Car метод с подписью: getMaxSpeed(Car car).


если вы хотите создать ссылку на метод для метода, который не принимает параметров, таких как метод, уже привязанный к экземпляру, вы должны использовать Supplier, а не Function:

Function<Car, Integer> f1 = Car::getMaxSpeed;

Car carx = new Car(42);
Supplier<Integer> f2 = carx::getMaxSpeed; 

в методе reference carX::getMaxSpeed в "неявные" this-параметр функции уже привязан к carx, таким образом, у вас остается функция без параметров (которая, кстати, не может быть использована в Comparator), а в Java 8 функция без параметров-это просто Supplier.

аналогично, если у вас есть метод, который возвращает void, вы в конечном итоге с помощью Comsumer:

Consumer<Integer> f3 = carx::setMaxSpeed;

функции-члена без параметров на самом деле имеет скрытый параметр,this ссылка. Ссылки на метод формы ClassName::memberFunction всегда используйте первый параметр функционального типа для экземпляра класса, т. е. скрытый экземпляр . Итак, в случае Car.getMaxSpeed(), внутренне он имеет те же параметры, что и static Integer getMaxSpeed(Car car). будет соответствовать функциональному типу Function<Car,Integer>, так как static Integer getMaxSpeed(Car car) будет.

что-то подобное происходит с членом функции, которые принимают один параметр--они соответствуют BiFunction функциональный тип, причем первым параметром является экземпляр класса.


назначение:

Function<Void, Integer> function = carX::getMaxSpeed;

не компилируется, потому что это Supplier<Integer>, а не Function.

тогда почему это компилируется?:

Comparator.comparing(Car::getMaxSpeed)

Java 8 позволяет ссылку на метод экземпляра, который является Supplier<U> быть обеспеченным где Function<T, U> ожидается, и компилятор эффективно преобразует метод getter в функцию.

чтобы узнать, почему это возможно, давайте посмотрим, как мы вызываем метод getter, используя отражение:

System.out.println(Car.class.getMethod("getMaxSpeed").invoke(carX)); // "155"

при вызове invoke() в методе экземпляра мы передаем экземпляр в invoke() метод геттера Method - существует подразумеваемый параметр типа экземпляра. Когда мы смотрим на это таким образом, мы видим, что под капотом геттер действительно реализован как Function<T, U> через invoke() метод.


давайте посмотрим на функции подробнее:

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

}

Примечание R apply(T t); применяет эту функцию к данному аргументу.

Function<Void, Integer> function = Void::?????;
Void voidInstance = null;
function.apply(voidInstance);

это не имеет смысла. Вы хотите пройти пустоту, чтобы применить функцию пустоты ?

несколько иллюстративных примеров того, что компилируется как функция

отметим, что c->c.getMaxSpeed() и Car::getMaxSpeed синтаксически эквивалентны, если метод является instanceMethod. Для non-static-methods первый аргумент выводится из типа, в котором используется метод who, и должен быть предоставлен позже (как экземпляр, на котором метод будет выполняться/применяться).

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