Ссылка на метод - передача функции в метод с аргументом Consumer - PullRequest
0 голосов
/ 16 мая 2018

Я изучаю ссылки на методы из Java 8, и мне трудно понять, почему это работает?

class Holder {
    private String holded;

    public Holder(String holded) {
        this.holded = holded;
    }

    public String getHolded() {
        return holded;
    }
}

private void run() {
    Function<Holder, String> getHolded = Holder::getHolded;

    consume(Holder::getHolded); //This is correct...
    consume(getHolded);         //...but this is not
}

private void consume(Consumer<Holder> consumer) {
    consumer.accept(null);
}

Как вы можете видеть в run метод - Holder::getHolded возвращает ссылку на несвязанный метод, котораяВы можете вызвать, передав объект типа Holder в качестве аргумента.Например: getHolded.apply(holder)

Но почему он переводит эту несвязанную ссылку на метод на Consumer, когда он вызывается непосредственно в качестве аргумента метода, и не делает этого, когда я передаю Function явно?

Ответы [ 2 ]

0 голосов
/ 16 мая 2018

Java 8 представила 4 важных «формы функций» в пакете java.util.function.

  • Consumer -> принимает ссылку на метод (или лямбда-выражение), которая принимает один аргумент, но ничего не возвращает
  • Поставщик -> принимает ссылку на метод (или лямбда-выражение), которая не принимает аргументов и возвращает объект.
  • Функция -> принимает ссылку на метод (или лямбда-выражение), которая принимает один аргумент и возвращает объект.
  • Predicate -> принимает ссылку на метод (или лямбда-выражение), которая принимает один аргумент и возвращает логическое значение.

Подробнее читайте в Java документах .

Чтобы ответить на ваш вопрос о том, почему первый работает, а второй выдает ошибку, прочитайте следующее:

Второе утверждение

consume(getHolded);

не работает, потому что тип аргумента getHolded равен Function<Holder, String>, тогда как метод consume ожидает аргумент типа Consumer<Holder>. Поскольку между Function и Consumer нет отношения родитель-потомок, требуется явное приведение, без которого компилятор правильно выдает ошибку.

Первое утверждение

consume(Holder::getHolded);

работает, потому что метод getHolded объявлен как public String getHolded(), что означает, что он не принимает никаких аргументов и возвращает String. Согласно новому правилу void compatibility , типы void выводятся как класс, содержащий указанный метод. Рассмотрим следующее утверждение:

Consumer<Holder> consumer = Holder::getHolded;

Это допустимое утверждение, хотя метод getHolded не принимает никаких аргументов. Это позволяет облегчить вывод типов пустот. Еще один пример, который вы упомянули сами:

Function<Holder, String> getHolded = Holder::getHolded;

Это также допустимое утверждение, в котором вы сказали, что объект функции getHolded - это Function, который возвращает String и принимает тип Holder, даже если назначенная ссылка на метод не принимает аргументов.

0 голосов
/ 16 мая 2018

Здесь две вещи, лямбда-выражения являются поли-выражениями - они выводятся компилятором с использованием их контекста (например, обобщений).

Когда вы объявляете consume(Holder::getHolded);, компилятор (под так называемым специальным правилом совместимости void ) будет выведено значение Consumer<Holder>.

И это может показаться неочевидным, но представьте себе упрощенный пример.Обычно это более чем нормально, вызывать метод и отбрасывать его тип возвращаемого значения, верно?Например:

List<Integer> list = new ArrayList<>();
list.add(1);

Даже если list.add(1) возвращает логическое значение, нас это не волнует.

Таким образом, ваш работающий пример можно упростить до:

consume(x -> {
        x.getHolded(); // ignore the result here
        return;
});

Итак, это и возможные, и правильные объявления:

Consumer<Holder> consumer = Holder::getHolded;
Function<Holder, String> function = Holder::getHolded;

Но в этом случае мы явно сообщаем, какой тип Holder::getHolded, это не вывод компилятора,таким образом consume(getHolded); терпит неудачу, а Consumer! = Function в конце концов.

...