使用带有参数的方法引用

51

我刚开始学习Java流并遇到了一个问题。请看下面的例子,代码是Node类的一部分:

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方法引用,但没有成功。我会欣赏其他解决方案而不是使用->运算符。

有没有可能在方法引用中同时使用参数?我喜欢流的想法,我只想使代码更易读。

实际上,我认为有一个类似的问题Method references with a parameter,我已经阅读了它,但无法弄清如何在我的代码中使用bind2方法。这是唯一的解决方案吗?


1
只有在需要具有类似签名的函数接口时,才能传递方法引用(即可以推断出lambda的参数)。 - Alex Salauyou
forEach() 函数期望一个单一的 Node 参数并返回 void,因此只能传递到接受单一 Node 或无参数且返回 void 的 Node 类的静态方法引用。解决方案:自己创建这样的方法。 - Alex Salauyou
https://marcin-chwedczuk.github.io/method-references-in-java-8 是一些很好的例子,供那些想要了解带参数方法引用的人参考。 - anuj pradhan
1个回答

53
你不能使用方法引用来实现这个目的,你需要使用lambda表达式。这个链接问题中的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)); 更简单吗?


谢谢!如何将带有2个参数的Node::findChildren转换为接受3个参数的ThreeConsumer? - matepal297
4
对于非静态方法,您必须将方法接收器视为参数进行计数。在您特定的情况下,使用ClassName :: methodName引用实例方法时,消费者将使用节点实例作为参数调用,该参数将被传递到findChildren方法中。相反,您可以通过说object :: methodName将方法引用绑定到特定的接收器,然后该方法将在object上调用。例如,当您说System.out :: println时,方法println将在System.out中找到的PrintStream实例上调用。 - Holger
1
但在您的情况下,您想要处理不同的“Node”实例流,因此将接收器保持未绑定并将其视为参数进行计数是您想要的。请参见这里 - Holger
4
又过了一个让人头皮发麻的小时才发现竟然是“你做不到”。 - Mihai Morcov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接