Использовать ссылку на метод с параметром

Я только начал изучать потоки Java и столкнулся с проблемой. Пожалуйста, взгляните на следующий пример. Это часть класса узла:

private Map<String, Node> nodes;

public Optional<Node> child(String name) {
    return Optional.<Node>ofNullable(nodes.get(name));
}

private void findChildren(String name, List<Node> result) {
    child(name).ifPresent(result::add);
    nodes.values().stream()
//          .map(Node::findChildren(name, result))
//          .forEach(Node::findChildren(name, result))
            .forEach(node -> node.findChildren(name, result));
}

Мое намерение состояло в том, чтобы вызвать #findChildren с параметрами имени и результата на каждом узле в потоке. Я безуспешно пытался использовать ссылки на методы Node::findChildren. Я был бы признателен за решения, отличные от решения с оператором ->.

Можно ли как-то использовать ссылку на метод вместе с параметром? Мне нравится идея потоков, и я просто хочу сделать код более читабельным.

На самом деле, я думаю, что есть похожий вопрос ссылки на метод с параметром, который я читал, но не могу понять, как использовать метод bind2 в моем коде. Это единственное решение?


person matepal297    schedule 23.04.2015    source источник
comment
Вы можете передавать ссылки на методы только там, где требуются функциональные интерфейсы с аналогичной сигнатурой (т.е. можно вывести аргументы для лямбда)   -  person Alex Salauyou    schedule 24.04.2015
comment
forEach() ожидает один аргумент Node и возвращает void, поэтому туда может быть передана только ссылка на статический метод, принимающий одиночный Node, или метод без аргументов класса Node с возвратом void. Решение: создать такой метод самостоятельно.   -  person Alex Salauyou    schedule 24.04.2015
comment
marcin-chwedczuk.github.io/method-references-in-java- 8 — несколько хороших примеров для тех, кто ищет ссылки на методы с аргументами.   -  person anuj pradhan    schedule 02.04.2018


Ответы (1)


Вы не можете использовать ссылки на методы для этой цели. Вы должны прибегнуть к лямбда-выражениям. Причина, по которой метод bind2 связанного вопроса не работает, заключается в том, что вы на самом деле пытаетесь связать два параметра, чтобы преобразовать функцию с тремя аргументами в функцию с одним аргументом. Столь же простого решения нет, так как нет стандартного функционала interface для потребителей с тремя аргументами.

Это должно было бы выглядеть как

interface ThreeConsumer<T, U, V> {
    void accept(T t, U u, V v);
}
public static <T, U, V> Consumer<T> bind2and3(
                        ThreeConsumer<? super T, U, V> c, U arg2, V arg3) {
    return (arg1) -> c.accept(arg1, arg2, arg3);
}

Тогда .forEach(bind2and3(Node::findChildren, name, result)); может работать. Но действительно ли это проще, чем .forEach(node -> node.findChildren(name, result));?

person Holger    schedule 24.04.2015
comment
Благодарю вас! Как преобразователь Node::findChildren с 2 параметрами в ThreeConsumer, который принимает 3 параметра? - person matepal297; 24.04.2015
comment
Для методов, отличных от static, вы должны учитывать получателя метода как параметр. В вашем конкретном случае потребитель вызывается с экземпляром Node в качестве параметра, для которого будет вызываться метод findChildren. Что ж, это применимо, если вы используете ClassName::methodName для ссылки на метод экземпляра. Вместо этого вы можете привязать ссылку на метод к конкретному приемнику, сказав object::methodName, тогда метод будет вызван object. Это происходит, например. когда вы говорите System.out::println, где метод println будет вызываться для экземпляра PrintStream, найденного в System.out. - person Holger; 24.04.2015
comment
Но в вашем случае вы хотите обработать поток разных экземпляров Node, поэтому вам нужно оставить получателя несвязанным и считать его параметром. См. также здесь - person Holger; 24.04.2015
comment
Еще один час плавления мозгов, чтобы узнать, что Ты не можешь. - person Mihai Morcov; 07.02.2017