Objects :: nonNull和x - >之间有什么区别吗? x!= null?

时间:2014-08-21 20:08:26

标签: java lambda java-8 method-reference

考虑以下课程:

import java.util.Objects;
import java.util.function.Predicate;

public class LambdaVsMethodRef {
    public static void main(String[] args) {
        Predicate<Object> a = Objects::nonNull;
        Predicate<Object> b = x -> x != null;
    }
}

第一个谓词是从方法引用创建的,另一个是lambda表达式。这些谓词具有相同的行为(nonNull的正文仅为return obj != null;)。 lambda短两个字符(可能允许流管道适合一行)。

除了代码样式, Objects::nonNullx -> x != null之间有什么区别吗?换句话说,我应该更喜欢一个吗?

提及isNullnonNullisNotNull(早期名称)的lambda-dev和lambda-libs-spec- {observers,experts}邮件列表消息没有解决这点。 (我很惊讶没有人质疑添加Objects方法,因为它们可以用lambda轻易替换,但另一方面,Integer::sum也是如此。)

我还查看了javap的字节码。唯一的区别是传递给lambda metafactory bootstrap method的方法句柄:

  BootstrapMethods:
0: #16 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  Method arguments:
    #17 (Ljava/lang/Object;)Z
    #18 invokestatic java/util/Objects.nonNull:(Ljava/lang/Object;)Z
    #17 (Ljava/lang/Object;)Z
1: #16 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  Method arguments:
    #17 (Ljava/lang/Object;)Z
    #20 invokestatic LambdaVsMethodRef.lambda$main$1:(Ljava/lang/Object;)Z
    #17 (Ljava/lang/Object;)Z

当然,对于方法引用和lambdas来说,元数据可以在JVM的奇思妙想中做不同的事情,所以这并不能证明这一点。

1 个答案:

答案 0 :(得分:29)

正如您所指出的,lambda x -> x != null和方法引用Objects::nonNull的语义几乎完全相同。我很难想到任何实际可观察​​到的差异,缺少使用反射挖掘课程,或类似的东西。

在lambda上使用方法引用有一个小的空间优势。使用lambda,lambda的代码被编译成包含类的私有静态方法,然后通过引用此静态方法调用lambda metafactory。在方法引用的情况下,该方法已经存在于java.util.Objects类中,因此lambda metafactory是通过引用现有方法来调用的。这样可以节省适度的空间。

考虑这些小班:

class LM { // lambda
    static Predicate<Object> a = x -> x != null;
}

class MR { // method reference
    static Predicate<Object> a = Objects::nonNull;
}

(感兴趣的读者应该运行javap -private -cp classes -c -v <class>来查看这些编译方式之间的详细差异。)

这导致lambda案例为1,094字节,方法参考案例为989字节。 (Javac 1.8.0_11。)这并不是一个很大的区别,但是如果你的程序可能有大量这样的lambdas,你可以考虑使用方法引用节省空间。

此外,方法引用更有可能是jit编译和内联而不是lambda,因为方法引用可能使用得更多。这可能会导致性能微小改善。但是,这似乎不太可能产生实际差异。

虽然你特别说'#34;除了代码风格......&#34;这真的主要是关于风格。这些小方法专门添加到API中,以便程序员可以使用名称而不是内联lambda。这通常会提高代码的可理解性。另一点是方法引用通常具有显式类型信息,可以帮助解决困难类型的推理案例,例如嵌套的Comparators。 (但这并不适用于Objects::nonNull。)添加强制转换或显式类型的lambda参数会增加很多混乱,因此在这些情况下,方法引用是明显的胜利。