Objects :: nonNull和x – > x!= null之间有什么区别吗?
考虑以下课程:
import java.util.Objects; import java.util.function.Predicate; public class LambdaVsMethodRef { public static void main(String[] args) { Predicate a = Objects::nonNull; Predicate b = x -> x != null; } }
第一个谓词是从方法引用创建的,另一个是lambda表达式。 这些谓词具有相同的行为( nonNull
的主体只是return obj != null;
)。 lambda短两个字符(可能允许流管道适合一行)。
除了代码风格, Objects::nonNull
和x -> x != null
之间有什么区别吗? 换句话说,我应该更喜欢一个吗?
lambda-dev和lambda-libs-spec- {观察者,专家}邮件列表消息提到isNull
, nonNull
和isNotNull
(早期名称)没有解决这一点。 (我很惊讶没有人质疑添加Objects方法,因为它们可以用lambda轻松替换,但另一方面, Integer::sum
也是如此。)
我还用javap
查看了字节码。 唯一的区别是传递给lambda metafactory bootstrap方法的方法句柄:
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
当然,对于方法引用和lambda来说,元数据可以做些不同的事情,这是JVM的一时兴起,所以这并不是很有效。
如您所述,lambda x -> x != null
的语义和方法引用Objects::nonNull
实际上是相同的。 我很难想到任何实际可观察到的差异,缺少使用reflection挖掘课程,或类似的东西。
在lambda上使用方法引用有一个小的空间优势。 使用lambda,lambda的代码被编译成包含类的私有静态方法,然后通过引用此静态方法调用lambda metafactory。 在方法引用案例中,该方法已经存在于java.util.Objects
类中,因此通过对现有方法的引用来调用lambda metafactory。 这样可以节省适度的空间。
考虑这些小class:
class LM { // lambda static Predicate
(感兴趣的读者应该运行javap -private -cp classes -c -v
来查看这些编译方式之间的详细差异。)
这导致lambda case为1,094字节,方法参考case为989字节。 (Javac 1.8.0_11。)这不是一个巨大的区别,但如果您的程序可能有大量像这样的lambdas,您可以考虑使用方法引用节省空间。
此外,方法引用更有可能是jIT编译和内联而不是lambda,因为方法引用可能使用得更多。 这可能会导致性能微小改善。 但是,这似乎不太可能产生实际差异。
虽然你特别说“除了代码风格……”,但这主要是关于风格。 这些小方法专门添加到API中,以便程序员可以使用名称而不是内联lambda。 这通常会提高代码的可理解性。 另一点是方法引用通常具有显式类型信息,可以帮助处理困难类型的推理案例,例如嵌套的Comparators。 (但这并不适用于Objects::nonNull
。)添加Objects::nonNull
或显式类型的lambda参数会增加很多混乱,因此在这些情况下,方法引用是明显的胜利。