Evaluation of Methods References in Java – 评估Java中的方法引用

最后修改: 2019年 8月 11日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

Java 8 introduced the concept of method references. We often see them as similar to lambda expressions.

Java 8引入了方法引用的概念。我们经常看到它们类似于lambda表达式。

However, method references and lambda expressions are not exactly the same thing. In this article, we’ll show why they are different and what are the risks of using them in the wrong way.

然而,方法引用和lambda表达式不完全是一回事。在这篇文章中,我们将说明为什么它们是不同的,以及以错误的方式使用它们有哪些风险。

2. Lambdas and Method References Syntax

2.兰姆达和方法引用语法

To start with, let’s see a few examples of lambda expressions:

首先,让我们看一下λ表达式的几个例子。

Runnable r1 = () -> "some string".toUpperCase();
Consumer<String> c1 = x -> x.toUpperCase();

And a few examples of method references:

还有一些方法参考的例子。

Function<String, String> f1 = String::toUpperCase;
Runnable r2 = "some string"::toUpperCase;
Runnable r3 = String::new;

Those examples could make us think about method references as a shortened notation for lambdas.

这些例子可以让我们考虑将方法引用作为lambdas的一个简短的符号。

But let’s take a look at the official Oracle documentation. We can find an interesting example there:

但是,让我们看一下官方的Oracle文档。我们可以在那里找到一个有趣的例子。

(test ? list.replaceAll(String::trim) : list) :: iterator

As we can see, the Java Language Specification allows us to have a different kind of expressions before double colon operator. The part before the :: is called the target reference.

我们可以看到,Java语言规范允许我们在双冒号运算符之前有不同的表达方式。在::之前的部分被称为thetarget reference

Next, we’ll discuss the process of method reference evaluation.

接下来,我们将讨论方法参考评价的过程。

3. Method Reference Evaluation

3.方法参考评价

What is going to happen when we run the following code?

当我们运行以下代码时,会发生什么?

public static void main(String[] args) {
    Runnable runnable = (f("some") + f("string"))::toUpperCase;
}

private static String f(String string) {
    System.out.println(string);
    return string;
}

We’ve just created a Runnable object. Nothing more, nothing less. However, the output is:

我们刚刚创建了一个Runnable对象。没有别的,也没有别的。然而,输出的结果是。

some
string

It happened because the target reference is evaluated when the declaration is first spotted. Hence, we’ve lost the desired laziness. The target reference is also evaluated only once. So if we add this line to the above example:

它的发生是因为目标引用是在第一次发现声明时被评估的。因此,我们失去了想要的懒惰性。目标引用也只被评估一次。所以如果我们在上面的例子中加入这一行。

runnable.run()

We will not see any output. What about the next case?

我们不会看到任何输出。那下一个案例呢?

SomeWorker worker = null;
Runnable workLambda = () -> worker.work() // ok
Runnable workMethodReference = worker::work; // boom! NullPointerException

The explanation provided by the documentation mentioned before:

前面提到的文档提供的解释。

“A method invocation expression (§15.12) that invokes an instance method throws a NullPointerException if the target reference is null.”

“一个调用实例方法的方法调用表达式(§15.12)在目标引用为空时抛出一个NullPointerException。”

The best way to prevent unexpected situations might be to never use variable access and complex expressions as target references.

防止意外情况的最好方法可能是永远不要使用变量访问和复杂表达式作为目标引用

A good idea might be to use method references only as a neat, short notation for its lambda equivalent. Having just a class name before :: operator guarantees safety.

一个好的想法可能是只使用方法引用作为其lambda等价物的一个整洁、简短的符号。在:.操作符之前只有一个类的名字,可以保证安全。操作符保证安全。

4. Conclusion

4.总结

In this article, we’ve learned about the evaluation process of method references.

在这篇文章中,我们已经了解了方法参考的评估过程。

We know the risks and the rules we should follow to not be suddenly surprised by the behavior of our application.

我们知道风险和我们应该遵循的规则,以便不被我们的应用程序的行为所突然惊奇。