10个Java8Lambda表达式经典示例Java8刚于几周前发布,日期是2014年3月18日,这次开创性的发布在Java社区引发了不少讨论,并让大家感到激动。特性之一便是随同发布的lambda表达式,它将允许我们将行为传到函数里。在Java8之前,如果想将行为传入函数,仅有的选择就是匿名类,需要6行代码。而定义行为最重要的那行代码,却混在中间不够突出。Lambda表达式取代了匿名类,取消了模板,允许用函数式风格编写代码。这样有时可读性更好,表达更清晰。在Java生态系统中,函数式表达与对面向对象的全面支持是个激动人心的进步。将进一步促进并行第三方库的发展,充分利用多核CPU。尽管业界需要时间来消化Java8,但我认为任何严谨的Java开发者都不应忽视此次Java发布的核心特性,即lambda表达式、函数式接口、流API、默认方法和新的Date以及TimeAPI。作为开发人员,我发现学习和掌握lambda表达式的最佳方法就是勇于尝试,尽可能多练习lambda表达式例子。鉴于受Java8发布的影响最大的是Java集合框架(JavaCollectionsframework),所以最好练习流API和lambda表达式,用于对列表(Lists)和集合(Collections)数据进行提取、过滤和排序。我一直在进行关于Java8的写作,过去也曾分享过一些资源来帮助大家掌握Java8。本文分享在代码中最有用的10个lambda表达式的使用方法,这些例子都短小精悍,将帮助你快速学会lambda表达式。Java8lambda表达式示例我个人对Java8发布非常激动,尤其是lambda表达式和流API。越来越多的了解它们,我能写出更干净的代码。虽然一开始并不是这样。第一次看到用lambda表达式写出来的Java代码时,我对这种神秘的语法感到非常失望,认为它们把Java搞得不可读,但我错了。花了一天时间做了一些lambda表达式和流API示例的练习后,我开心的看到了更清晰的Java代码。这有点像学习泛型,第一次见的时候我很讨厌它。我甚至继续使用老版Java1.4来处理集合,直到有一天,朋友跟我介绍了使用泛型的好处(才意识到它的好处)。所以基本立场就是,不要畏惧lambda表达式以及方法引用的神秘语法,做几次练习,从集合类中提取、过滤数据之后,你就会喜欢上它。下面让我们开启学习Java8lambda表达式的学习之旅吧,首先从简单例子开始。例1、用lambda表达式实现Runnable我开始使用Java8时,首先做的就是使用lambda表达式替换匿名类,而实现Runnable接口是匿名类的最好示例。看一下Java8之前的runnable实现方法,需要4行代码,而使用lambda表达式只需要一行代码。我们在这里做了什么呢?那就是用()-{}代码块替代了整个匿名类。//Java8之前:newThread(newRunnable(){@Overridepublicvoidrun(){System.out.println(BeforeJava8,toomuchcodefortoolittletodo);}}).start();//Java8方式:newThread(()-System.out.println(InJava8,Lambdaexpressionrocks!!)).start();输出:toomuchcode,fortoolittletodoLambdaexpressionrocks!!这个例子向我们展示了Java8lambda表达式的语法。你可以使用lambda写出如下代码:(params)-expression(params)-statement(params)-{statements}例如,如果你的方法不对参数进行修改、重写,只是在控制台打印点东西的话,那么可以这样写:()-System.out.println(HelloLambdaExpressions);如果你的方法接收两个参数,那么可以写成如下这样:(inteven,intodd)-even+odd顺便提一句,通常都会把lambda表达式内部变量的名字起得短一些。这样能使代码更简短,放在同一行。所以,在上述代码中,变量名选用a、b或者x、y会比even、odd要好。例2、使用Java8lambda表达式进行事件处理如果你用过SwingAPI编程,你就会记得怎样写事件监听代码。这又是一个旧版本简单匿名类的经典用例,但现在可以不这样了。你可以用lambda表达式写出更好的事件监听代码,如下所示://Java8之前:JButtonshow=newJButton(Show);show.addActionListener(newActionListener(){@OverridepublicvoidactionPerformed(ActionEvente){System.out.println(Eventhandlingwithoutlambdaexpressionisboring);}});//Java8方式:show.addActionListener((e)-{System.out.println(Light,Camera,Action!!LambdaexpressionsRocks);});Java开发者经常使用匿名类的另一个地方是为Collections.sort()定制Comparator。在Java8中,你可以用更可读的lambda表达式换掉丑陋的匿名类。我把这个留做练习,应该不难,可以按照我在使用lambda表达式实现Runnable和ActionListener的过程中的套路来做。例3、使用lambda表达式对列表进行迭代如果你使过几年Java,你就知道针对集合类,最常见的操作就是进行迭代,并将业务逻辑应用于各个元素,例如处理订单、交易和事件的列表。由于Java是命令式语言,Java8之前的所有循环代码都是顺序的,即可以对其元素进行并行化处理。如果你想做并行过滤,就需要自己写代码,这并不是那么容易。通过引入lambda表达式和默认方法,将做什么和怎么做的问题分开了,这意味着Java集合现在知道怎样做迭代,并可以在API层面对集合元素进行并行处理。下面的例子里,我将介绍如何在使用lambda或不使用lambda表达式的情况下迭代列表。你可以看到列表现在有了一个forEach()方法,它可以迭代所有对象,并将你的lambda代码应用在其中。//Java8之前:Listfeatures=Arrays.asList(Lambdas,DefaultMethod,StreamAPI,DateandTimeAPI);for(Stringfeature:features){System.out.println(feature);}//Java8之后:Listfeatures=Arrays.asList(Lambdas,DefaultMethod,StreamAPI,DateandTimeAPI);features.forEach(n-System.out.println(n));//使用Java8的方法引用更方便,方法引用由::双冒号操作符标示,//看起来像C++的作用域解析运算符features.forEach(System.out::println);输出:LambdasDefaultMethodStreamAPIDateandTimeAPI列表循环的最后一个例子展示了如何在Java8中使用方法引用(methodreference)。你可以看到C++里面的双冒号、范围解析操作符现在在Java8中用来表示方法引用。例4、使用lambda表达式和函数式接口Predicate除了在语言层面支持函数式编程风格,Java8也添加了一个包,叫做java.util.function。它包含了很多类,用来支持Java的函数式编程。其中一个便是Predicate,使用java.util.function.Predicate函数式接口以及lambda表达式,可以向API方法添加逻辑,用更少的代码支持更多的动态行为。下面是Java8Predicate的例子,展示了过滤集合数据的多种常用方法。Predicate接口非常适用于做过滤。publicstaticvoidmain(args[]){Listlanguages=Arrays.asList(Java,Scala,C++,Haskell,Lisp);System.out.println(LanguageswhichstartswithJ:);filter(languages,(str)-str.startsWith(J));System.out.println(Languageswhichendswitha);filter(languages,(str)-str.endsWith(a));System.out.println(Printalllanguages:);filter(languages,(str)-true);System.out.println(Printnolanguage:);filter(languages,(str)-false);System.out.println(Printlanguagewhoselengthgreaterthan4:);filter(languages,(str)-str.length()4);}publicstaticvoidfilter(Listnames,Predicatecondition){for(Stringname:names){if(condition.test(name)){System.out.println(name+);}}}输出:LanguageswhichstartswithJ:JavaLanguageswhichendswithaJavaScalaPrintalllanguages:JavaScalaC++HaskellLispPrintnolanguage:Printlanguagewhoselengthgreaterthan4:ScalaHaskell//更好的办法publicstaticvoidfilter(Listnames,Predicatecondition){names.stream().filter((name)-(condition.test(name))).forEach((name)-{System.out.println(name+);});}可以看到,StreamAPI的过滤方法也接受一个Predicate,这意味着可以将我们定制的filter()方法替换成写在里面的内联代码,这就是lambda表达式的魔力。另外,Predicate接口也允许进行多重条件的测试,下个例子将要讲到。例5、如何在lambda表达式中加入Predicate上个例子说到,java.util.function.Predicate允许将两个或更多的Predicate合成一个。它提供类似于逻辑操作符AND和OR的方法,名字叫做and()、or()和xor(),用于将传入filter()方法的条件合并起来。例如,要得到所有以J开始,长度为四个字母的语言,可以定义两个独立的Predicate示例分别表示每一个条件,然后用Predicate.and()方法将它们合并起来,如下所示://甚至可以用and()、or()和xor()逻辑函数来合并Predicate,//例如要找到所有以J开始,长度为四个字母的名字,你可以合并两个Predicate并传入PredicateStringstartsWithJ=(n)-n.startsWith(J);PredicateStringfourLetterLong=(n)-n.length()==4;names.stream().filter(startsWithJ.and(fourLetterLong