美文网首页
java8中一个超易踩的坑:方法引用与lambda表达式的区别

java8中一个超易踩的坑:方法引用与lambda表达式的区别

作者: agile4j | 来源:发表于2018-06-24 23:08 被阅读103次

    作者:刘仁鹏
    参考资料:

    1. java.lang.NullPointerException is thrown using a method-reference but not a lambda expression
    2. Run-Time Evaluation of Method References
    3. Run-Time Evaluation of Lambda Expressions

    1.IDEA在诱导我写bug

    • 看一下下面的代码,你认为它的输出会是什么?
    public class Test {
    
        @org.junit.Test
        public void test() throws InterruptedException {
            testNPEOfLambda(null);
            Thread.sleep(1000);
            testNPEOfMethodRef(null);
        }
    
        private static void testNPEOfLambda(MyPrinter printer) {
            testNPE(() -> printer.out());
        }
    
        private static void testNPEOfMethodRef(MyPrinter printer) {
            testNPE(printer::out);
        }
    
        private static void testNPE(Runnable runnable) {
            Thread t = new Thread(runnable);
            t.setUncaughtExceptionHandler((t1, e) ->
                    System.out.println(t1.getName() + " Exception!"));
            t.start();
        }
    
        static class MyPrinter {
            void out() {
                System.out.println("hello world");
            }
        }
    
    }
    
    
    • 可能你会说,testNPEOfLambdatestNPEOfMethodRef的区别不过是一个使用了lambda表达式,一个使用了方法引用,最终都是返回一个Runnable对象,结果肯定是一样的,类似下面这样:
    //输出:
    Thread-0 Exception!
    Thread-1 Exception!
    
    • 让我们来实际运行一下:
    //输出:
    Thread-0 Exception!
    
    java.lang.NullPointerException
        at com.lpcoder.agile.base.Test.testNPEOfMethodRef(Test.java:17)
        at com.lpcoder.agile.base.Test.test(Test.java:9)
        ...略
    
    • 这个结果和我们一开始预料的不一样啊,使用lambda表达式运行结果在意料之中,而使用方法引用则过早的就抛出了NPE。甚至IDEA都在我使用lambda表示式时给我提示:这里可使用方法引用。强烈暗示我这两种写法的效果是相同的
      lambda.png-23kBlambda.png-23kB

    2.为什么方法引用与lambda表达式不同

    First, if the method reference expression begins with an ExpressionName or a Primary, this subexpression is evaluated. If the subexpression evaluates to null, a NullPointerException is raised, and the method reference expression completes abruptly. If the subexpression completes abruptly, the method reference expression completes abruptly for the same reason.
    首先,如果方法引用表达式以ExpressionName或Primary开头,那么将对这个子表达式进行求值。如果子表达式的计算值为null,就会引发NullPointerException,方法引用表达式就会突然结束。如果子表达式突然结束,那么方法引用表达式也会因为同样的原因突然结束。

    Evaluation of a lambda expression is distinct from execution of the lambda body.
    对lambda表达式的求值与执行lambda正文是不同的。

    • 所以,方法引用和lambda表达式,是有区别的:方法引用会在运行时,会对::前的子表达式进行“预求值”,如果发现子表达式值为null,则抛出NPE。而lambda表达式不会,lambda表达式在运行时只会如常返回一个FunctionInterface实例,只有当真正运行lambda正文时,才会抛出NPE(缓求值)

    3.总结

    • 不要认为方法引用和lambda表达式是等价的(即使IDEA暗示你如此)
    • 方法引用会对::符号前的子表达式进行预求值,如果发现值为null,会立即抛出NPE
    • lambda表达式只有在真正运行lambda正文时,才会抛出NPE
    • 如果需要用到javaFunctionInterface的缓求值特性,使用lambda表达式,而不要使用方法引用,否则有提前抛出NPE的风险

    end

    相关文章

      网友评论

          本文标题:java8中一个超易踩的坑:方法引用与lambda表达式的区别

          本文链接:https://www.haomeiwen.com/subject/esxeyftx.html