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

时间:2016-10-10 11:24:25

标签: 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);
}

令我惊讶的是这一行:: object -> targetRef.equals(object);

也许我正在大力推翻这一点,但我无法立即思考为什么这条线不是: targetRef::equals;这样:

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

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

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

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

1 个答案:

答案 0 :(得分:9)

  

为什么这样做?

纯粹的推测,但是,也许在编写时,开发人员对 - &gt;更为舒适。并且可能::当时甚至没有工作。这些库是在修复编译器中的错误时编写的。

没有办法知道,甚至写作者也可能不记得。

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

  

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

使用方法引用意味着少一个方法调用。这可能会对内联产生间接影响,因为默认情况下级别数限制为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();
    }
}

打印

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)

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

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

e.g。

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

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