为什么Predicate.isEqual以它的方式实现?

Oll*_*man 8 java java-8

最近我一直在摆弄新的java8功能,以便更好地理解它们.

Stream.filter我尝试一些东西时,我遇到了Predicate.java其中我找到了以下isEqual方法实现的来源:

/**
 * Returns a predicate that tests if two arguments are equal according
 * to {@link Objects#equals(Object, Object)}.
 *
 * @param <T> the type of arguments to the predicate
 * @param targetRef the object reference with which to compare for equality,
 *               which may be {@code null}
 * @return a predicate that tests if two arguments are equal according
 * to {@link Objects#equals(Object, Object)}
 */
static <T> Predicate<T> isEqual(Object targetRef) {
    return (null == targetRef)
            ? Objects::isNull
            : object -> targetRef.equals(object);
}
Run Code Online (Sandbox Code Playgroud)

让我惊讶的是这条线:: object -> targetRef.equals(object);.

也许我正在大力推翻这个,但我不禁立刻想到为什么那条线不是: targetRef::equals;这样的:

static <T> Predicate<T> isEqual(Object targetRef) {
    return (null == targetRef)
            ? Objects::isNull
            : targetRef::equals;
}
Run Code Online (Sandbox Code Playgroud)

在我看来,不必要地创建一个lambda.

除非我遗漏了某些东西,否则我会做同样的事情.(他们是一样的吗?)有没有理由选择当前的实施?或者这只是一个非常小的东西,它只是被忽视或没有人真正关心.

猜猜实际上会产生一个额外的问题:使用一种方式比另一种方式有什么好处吗?像某种(可能是非常小的)性能奖金?

Pet*_*rey 9

为什么这样做?

纯粹的推测,但也许,当它被写入时,开发人员对 - >和可能的::当时甚至不起作用更为舒服.这些库是在修复编译器中的错误时编写的.

没有办法知道,甚至写它的人可能都不记得了.


无论使用方法引用还是闭包语法,在大多数情况下都会创建相同数量的对象(如本例所示)

猜猜实际上会产生一个额外的问题:使用一种方式比另一种方式有什么好处吗?像某种(可能是非常小的)性能奖金?

使用方法引用意味着少一个方法调用.这可能会对内联产生间接影响,因为默认情况下级别数限制为9.例如

import java.util.function.Consumer;

public class Main {
    public static void main(String[] args) {
        Consumer<String> lambda = s-> printStackTrace(s);
        lambda.accept("Defined as a lambda");

        Consumer<String> methodRef = Main::printStackTrace;
        methodRef.accept("Defined as a method reference");
    }


    static void printStackTrace(String description) {
        new Throwable(description).printStackTrace();
    }
}
Run Code Online (Sandbox Code Playgroud)

版画

java.lang.Throwable: Defined as a lambda
    at Main.printStackTrace(Main.java:15)
    at Main.lambda$main$0(Main.java:6)
    at Main.main(Main.java:7)

java.lang.Throwable: Defined as a method reference
    at Main.printStackTrace(Main.java:15)
    at Main.main(Main.java:10)
Run Code Online (Sandbox Code Playgroud)

在第一种情况下,编译器生成了一个Main.lambda$main$0包含实际调用的代码的方法printStackTrace

使用其中一个或另一个产生更大差异的地方在于您可以捕获(保存值)或不捕获lambda.非捕获lambda仅创建一次.

例如

Consumer<String> print1 = System.out::println; // creates an object each time
Consumer<String> print2 = s->System.out.println(s); // creates an object once.
Run Code Online (Sandbox Code Playgroud)

在第一种情况下,如果您调用System.setOut它将忽略此更改,因为它具有要写入的PrintStream的自己的副本.

  • @MarkoTopolnik采用lambda样式语法,将`static`方法添加到包含实际代码的类中,并将对此生成方法的方法引用添加到字节代码中.如果使用方法引用语法,则不会添加此额外的静态方法,这意味着内联的级别较少. (2认同)
  • 由于表达式包含一个`Objects :: isNull`,因此作者"不太可能对 - >"感觉更舒服,而且::不起作用.可能是`instance :: method`表单对作者来说不起作用或不知道,但似乎有一个对::运算符的偏好,否则它将是`object - > Objects.isNull(object)` ... (2认同)