• Lambda表达式


    Java 8历时2年8个月,这次升级是继Java 5之后对Java一次脱胎换骨的变化。在Java 8的新特性中很多都是围绕Lambda表达式而提供的,Lambda表达式也将使热衷于OOP(Object-Oriented Programming)的Java程序员体会到FP(Functional Programming)的强大。Java 8的Lambda表达多少借鉴了Scala的Lambda(Scala使用「=>」Java8使用「->」),和纯正FP的Haskell还是有很大差别。 

    (1)历史来由 

    ①关于Java7/Java8 
    Java 8的很多新特性按照预定都应该包含在Java 7中,但是由于对Project Lambda和Project Jigsaw的争论一直没有达成共识,Java 7一直无法发布。2010年9月Java平台首席架构师Mark Reinhold在他的Blog上提出2个方案: 

    • A:所有功能都包含到Java 7中,发布推迟到2012年中旬
    • B:不包含Project Lambda和Project Jigsaw等几个项目Java 7在2011年中旬发布,这几个项目推迟到2012年中旬发布的Java 8中

    博客的评论基本上都赞成方案B。2010年的JavaOne上Mark Reinhold正式决定采用方案B。之后2011年7月Java 7正式发布。 

    2012年7月 Mark Reinhold再次发文称Project Jigsaw将不会包含在Java 8中而得推迟到Java 9中,由此一来Java 8的主要特性就是Project Lambda。 

    2012年下旬->2013年下旬->2014年3月 Java 8的发布也是不断跳票。Project Coin、Collection Literals、Swing Date Picker等也被推迟到Java 9,而把Date & Time API、Nashorn等纳入Java 8中。 

    2014年3月18日 Java 8带着“即使有Bug也要发布”的节奏,历时2年8个月终于发布了。JavaTM SE 8 Release Contents 

    ②关于Project Lambda 
    2005年,Java 6发布前很多人提议在Java中引入闭包Closure,先是Gilad Bracha、Neal Gafter、James Gosling、Peter van der Ahé做了一个Java闭包的提案,叫BGGA(以他们名字的首字母命名)、后来改名Closures for the Java Programing Language。关于这个提案在Java社区掀起了很到争论,Neal Gafter和Joshua Bloch争论最为有名。 

    除BGGA外,Java闭包的实现也有很多其他的方案,比如:Joshua Bloch、Doug Lea、Bob Lee提出了CICE(Concise Instance Creation Expresions)。还有一个提案是FCM(First Class Methods)。CICE并不是要在Java中引入闭包而是对匿名类做一个简化写法的提案(现在最终的Project Lambda也是很接近CICE)。 

    2008年在java.net做了一个在线调查,大概2000人参与回答,大部分人投给了BGGA或者干脆不引入闭包。 

    2008年11月,Mark Reinhold在 Devoxx 发布Java 7中不会引入闭包,从而才终止了这场争论。但是1年后的2009年11月Mark Reinhold同样是在 Devoxx 发布Java 7中要引入简单的闭包。基于此,2009年12月作为OpenJDK的子项目Project Lambda开始启动。 

     

    为什么2008年终止在Java 7中引入闭包会在2009年再次提出? 
    答案是:CPU多核化的趋势。无论是PC还是SmartPhone,CPU的多核化在急速发展,所以软件的并行处理也要跟得上。 

    Java中虽然可以通过Thread来并行处理,但是Java 5的Concurrency Utilities的处理粒度更大,而Java 7的Fork/Join Framework并行粒度更细。Java的for循环采用外部迭代(程序员自己写代码),需要转成内部迭代(由Java类库迭代)的并行化,而很多语言都已经支持内部迭代。虽然Java可以借助匿名类来实现,但是写法很繁琐,所以需要简化匿名类的写法。 

    2009年Project Lambda成立后,Mark Reinhold提出了一个原案Straw-Man Proposal。在这个稻草人提议中Mark Reinhold并没有使用以前争论不休的闭包,而是采用了Lambda表达式。基于此草案不断演化才得到了现在Java8的Lambda表达式。JSR 335: Lambda Expressions for the Java™ Programming Language: 

    接口的变化: 
    开始决定使用SAM(Single Abstract Method)来作为接口,SAM指只有一个方法的抽象类或接口。但函数型的引入又激起了争论,后来只限于接口,所以很少人再说SAM,而是引入了函数式接口的概念。 

    写法的变化: 
    #(参数) { 函数体 } -> (参数) -> { 函数体 } 
    Collections.xxxx() -> Stream API 

    实现的变化 
    匿名类 ->Java 7 引入的 InvokeDynamic命令动态生成Class 

    Project Lambda是一个漫长的过程,能够最终成行已经不易,看看这些人的名字,他们都是Java界的大神,尤其Doug Lea为Java并发做出了不可磨灭的贡献。 

    (2)Lambda表达式 

    λ:希腊字母表中排序第十一位的字母,英语名称为Lambda。最早出现是用于计算的λ演算(lambda calculus),后来被函数式编程语言广泛采用。 

    Lambda表达式可以理解成为是一个能够作为参数传递的匿名函数Object,他没有名字,但有参数列表、有函数体、有返回类型、可以抛出异常。它的类型,叫做“目标类型(target type)”Java8中就是“函数接口(functional interface)”。 

    语法: 

    引用
    (parameters) -> expression 
    (parameters) -> statement 
    (parameters) -> { statements; }



    举例: 

    引用
    () -> Math.PI * 2.0 
    (int i) -> i * 2 
    (String s) -> s.length() 
    (int i0, int i1) -> i0 + i1 
    (int x, int y) -> { return x + y; }



    ①省略类型 

    Java代码  收藏代码
    1. (int x, int y) -> { return x + y; };  
    2. (x, y) -> { return x + y; };  


    ***不能只省略一部分类型,比如:(int x, y) -> { return x + y; }; // NG 

    ②1个参数可以省略括号 

    Java代码  收藏代码
    1. (String text) -> { System.out.println(text); };  
    2. (text) -> { System.out.println(text); };  
    3. text -> { System.out.println(text); };  


    ***但是不能带类型,比如:String text -> { System.out.println(text); }; // NG 

    ③函数体多行时需要大括号 

    Java代码  收藏代码
    1. (int i) -> {  
    2.     int prod = 1;  
    3.     for(int n = 0; n < 5; n++) prod *= i;  
    4.     return prod;  
    5. };  



    ④函数体只有一行的话可以省略大括号 

    Java代码  收藏代码
    1. text -> System.out.println(text);  


    ***assert语句不能省略带括号,比如: s -> { assert !s.isEmpty(); }; 
    ***return语句不能省略带括号,比如:(x, y) -> return x -y; // NG 

    ⑤只有一行代码而且有返回值的可以省略return,会返回该行代码计算结果 

    Java代码  收藏代码
    1. (x, y) -> { return x -y; };  
    2. (x, y) -> x -y;  


    ***return要和大括号一起省略,比如:(x, y) -> { x - y; }; // NG 

    ⑥没有参数没有返回值的空函数 

    Java代码  收藏代码
    1. () -> {};   



    ⑦Scala等使用下划线做占位符,Java8中不可以 

    Java代码  收藏代码
    1. (_) -> System.out.println(_); // NG  



    ⑧用于Lambda的变量不可变 

    Java代码  收藏代码
    1. int portNumber = 1337;  
    2. Runnable r = () -> System.out.println(portNumber); // OK  
    3.   
    4. // 编译错误  
    5. // Local variable portNumber defined in an enclosing scope must be final or effectively final  
    6. int portNumber = 1337;  
    7. Runnable r = () -> System.out.println(portNumber); // NG  
    8. portNumber = 1338;  
    9.   
    10. // 通过数组实现  
    11. final int[] wrappedNumber = new int[] { 1337 };  
    12. Runnable r = () -> System.out.println(wrappedNumber[0]); // OK  
    13. wrappedNumber[0] = 1338;  



    ⑨其他 

    • 一个λ表达式可以有多个目标类型(函数接口),只要函数匹配成功即可。但需注意一个λ表达式必须至少有一个目标类型。
    • 一个λ表达式可以被当做Object使用,需要赋值给一个函数接口,然后再赋值给一个Object。


    (3)Lambda和匿名类区别 
    this:匿名类this取得是自己,Lambda取得是所在的外部类。 

    Java代码  收藏代码
    1. public class LambdaTest {  
    2.     public LambdaTest() {  
    3.         Function func1 = new Function() {  
    4.             @Override  
    5.             public void func() {  
    6.                 System.out.println("Anon Class: " + this.getClass());  
    7.             }  
    8.         };  
    9.         Function func2 = () -> System.out.println("Lambda Exp.: " + this.getClass());  
    10.     }  
    11. }  


    ****但是很少需要在lambda中使用this 

    (4)用于何处 
    Lambda可以传递给任何希望是函数式接口的地方。 

    (5)运行机制 
    Java 7引入了invokedynamic指令,它是一个JVM指令,允许动态语言在run-time时动态绑定。Java 8的Lambda表达式并不是匿名类的语法糖,它不会在编译的时候生成类似于匿名类的xxx$1.class,而是在运行的时候使用invokeDynamic指令。对于一条Lambda表达式在class里边会包含一个invokedynamic命令和一个静态方法。运行时会使用LambdaMetafactory#metafactory做成一个Lambda$1的内部类再调用该函数式接口的实例。在运行时生成class,就是避免class太多影响加载速度,像Stream那样的到处是Lambda。 



    比如遍历List: 

    Java代码  收藏代码
    1. Arrays.asList("a", "b", "c").forEach(x -> System.out.println(x));  



    编译后的Class内容: 
     

     

    也可以接住异常堆栈信息看看Lambda是怎么执行的: 

    Java代码  收藏代码
    1. String[] datas = new String[]{""};  
    2. Arrays.asList(datas).stream().forEach(name -> check(name));  
    3.   
    4. public static int check(String s) {  
    5.     if (s.equals("")) {  
    6.         throw new IllegalArgumentException();  
    7.     }  
    8.     return s.length();  
    9. }  



    引用
    Exception in thread "main" java.lang.IllegalArgumentException 
        at com.rensanning.java8.lambda.Test.check(Test.java:14) 
        at com.rensanning.java8.lambda.Test.lambda$0(Test.java:9) 
        at com.rensanning.java8.lambda.Test$$Lambda$1/518248.accept(Unknown Source) 
        at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948) 
        at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580) 
        at com.rensanning.java8.lambda.Test.main(Test.java:9)



    参考: 
    http://www.oracle.com/events/us/en/java8/index.html 
    http://www.lambdafaq.org/ 
    http://cr.openjdk.java.net/~briangoetz/lambda/lambda-translation.html 
    http://www.oracle.com/webfolder/technetwork/tutorials/obe/java/Lambda-QuickStart/index.html 
    http://www.dreamsyssoft.com/java-8-lambda-tutorial/index.php 
    http://www.slideshare.net/bitter_fox/java8-launch 
    http://www.slideshare.net/miyakawataku/lambda-meets-invokedynamic 
    http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html 
    http://www.slideshare.net/mariofusco/fp-in-java-project-lambda-and-beyond 
    http://www.journaldev.com/2389/java-8-features-for-developers-lambdas-functional-interface-stream-and-time-api

  • 相关阅读:
    redhat yum替换成CentOS yum 并修改源
    C++11新特性实验
    常见的安装包制作程序installer
    如何在数据库中删除并添加唯一索引?
    springboot中的restTemplate工具类
    如何使用swagger(一)
    The POM for com.qingmu:entity:jar:1.0.0-SNAPSHOT is missing, no dependency information available
    java.lang.IllegalStateException: Found multiple @SpringBootConfiguration annotated classes
    在使用postman中配置返回html页面
    Springboot中设置返回数据的时间格式
  • 原文地址:https://www.cnblogs.com/zkycode/p/6251651.html
Copyright © 2020-2023  润新知