Java 8中的Lambda表达式(基于Java 8 Tutorial)
来源:互联网 发布:sai软件多大 编辑:程序博客网 时间:2024/05/17 09:16
参考地址:http://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html
本文档下载地址:点击打开链接
说明:主要讲解java 8中的Lambda表达式,还有一些functional interface的内容。内容完全基于java 8 tutorial,加上一些自己的注释与理解。
使用代码本身来进行解释(这是java 8 tutorial中的风格),同时去掉一些无关紧要的知识点(比如泛型等),并且增加了一些自己的必要的注释,
相信更易于大家理解。
作者:zhrb from jmu
注意:
1.页面查看效果很差,还是下载word附件进行查看。
2.在word中打开“导航窗格”或”文档结构图”更方便查看。
Lambda Expressions
应用背景:现在希望对List<Person> roster中符合条件的Person进行一些操作,比如输出姓名等..
Approach 1: Create Methods That Search forMembers That Match One Characteristic
public static void printPersonsOlderThan(List<Person> roster, int age) { for (Person p : roster) { if (p.getAge() >= age) { p.printPerson(); } }}
Approach 2: Create More Generalized SearchMethods
public static void printPersonsWithinAgeRange( List<Person> roster, int low, int high) { for (Person p : roster) { if (low <= p.getAge() && p.getAge() < high) { p.printPerson(); } }}
Approach 3: Specify Search Criteria Code ina Local Class
public static void printPersons( List<Person> roster, CheckPerson tester) { for (Person p : roster) { if (tester.test(p)) { p.printPerson(); } }}interface CheckPerson { boolean test(Person p);}class CheckPersonEligibleForSelectiveService implements CheckPerson { public boolean test(Person p) { return p.gender == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25; }}
使用:printPersons(roster, new CheckPersonEligibleForSelectiveService());
Approach 4: Specify Search Criteria Code inan Anonymous Class
printPersons( roster, new CheckPerson() { public boolean test(Person p) { return p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25; } });
Approach 5: Specify Search Criteria Codewith a Lambda Expression
printPersons( roster, (Person p) -> p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25);
Approach 6: Use Standard FunctionalInterfaces with Lambda Expressions
interface CheckPerson { boolean test(Person p);}interface Predicate<T> {//functional interface boolean test(T t);}
可以看出CheckPerson接口与Predicate<T>接口中定义的方法,都是传递进一个类型,返回boolean,形式上基本一致。
我们现在使用Predicate<T>接口取代CheckPerson接口(好处:少写一个接口,直接用通用接口)修改代码如下:
方法定义
public static void printPersonsWithPredicate( List<Person> roster, Predicate<Person> tester) { for (Person p : roster) { if (tester.test(p)) { p.printPerson(); } }//使用匿名内部类}
不需要再自定义一个CheckPerson接口,直接使用Predicate<Person>即可。
方法调用:
printPersonsWithPredicate( roster, p -> p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25);
Approach 7: Use Lambda ExpressionsThroughout Your Application
<pre code_snippet_id="315621" snippet_file_name="blog_20140427_9_7239644" name="code" class="java">public static void printPersonsWithPredicate( List<Person> roster, Predicate<Person> tester) { for (Person p : roster) { if (tester.test(p)) { p.printPerson(); } }}
上面的所有代码,对符合条件的Person只能print,如果需要进行更多的操作,怎么办?
方法定义:
public static void processPersons( List<Person> roster, Predicate<Person> tester, Consumer<Person> block) { for (Person p : roster) { if (tester.test(p)) { block.accept(p);//一个没有返回值的操作 } }}</pre>其中Consumer<Person>也是jdk中的functional interface,有一个方法void accept(T t),这个方法的特点刚好和printPerson()一样,没有任何的返回值
(原理类似于Approach 6中讲的内容)。上述定义也适合任何没有返回值的操作,
改进的方法调用:
processPersons( roster, p -> p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25, p -> p.printPerson()//实际上,这行还可以换成其他操作);可以看到processPersons不仅可以printPerson还可以调用其他方法,只要该方法的形式符合void accept(T t)….即1.无返回值。2.接受一个类型的参数。
进一步的,如果不仅要对符合条件的person进行“一个没有返回值的操作”,如上面的block.accept(p),还希望对他进行一些有返回值的操作,则可以使用Function<T,R>这个functional interface,该接口包含一个R apply(T t),如定义Function<Person, String>,那么就包含一个String apply(Person p)这样的操作。也就是说可以对符合条件的用户,执行一些操作并返回一个String值。
修改的定义如下:
public static void processPersonsWithFunction( List<Person> roster, Predicate<Person> tester, Function<Person, String> mapper, Consumer<String> block) { for (Person p : roster) { if (tester.test(p)) { String data = mapper.apply(p);//有返回值的操作 block.accept(data);//无返回值的操作 } }}调用如下:
processPersonsWithFunction( roster, p -> p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25, p -> p.getEmailAddress(),//有返回值的操作 email -> System.out.println(email) //无返回值的操作);<span style="background-color: rgb(255, 255, 255); font-family: Arial, Helvetica, sans-serif;">Approach 8: Use Generics More Extensively</span>更通用的写法,比如下面这个函数,可以针对所有实现Iterable接口的实现类(如列表,数组)进行Predicate、Function与Consumer操作。不过通用是通用,但我觉得一般还是不要这么写。因为要有许多前期知识(如functional interface,泛型)才能看懂这个代码,降低代码的可阅读性。
方法定义:
public static <X, Y> void processElements( Iterable<X> source, Predicate<X> tester, Function <X, Y> mapper, Consumer<Y> block) { for (X p : source) { if (tester.test(p)) { Y data = mapper.apply(p); block.accept(data); } }}方法调用:
processElements( roster, p -> p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25, p -> p.getEmailAddress(), email -> System.out.println(email));Approach 9: Use Aggregate Operations ThatAccept Lambda Expressions as Parameters
将Lambda当做参数…
roster.stream().filter( p -> p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25) .map(p -> p.getEmailAddress()) .forEach(email -> System.out.println(email));Lambda Expressions in GUI Applications
btn.setOnAction(new EventHandler<ActionEvent>() {//匿名类 @Override public void handle(ActionEvent event) { System.out.println("Hello World!"); } });改进,使用Lambda
btn.setOnAction( event -> System.out.println("Hello World!") );Syntax of Lambda Expressions
一个Lambda表达式由以下几部分构成:
1.A comma-separated list of formalparameters enclosed in parentheses.
一个用逗号分隔的形参列表,要用两个圆括号包起来。前面的例子,p就是形式参数,因为只有一个形参,所以不需要使用括号。不需要指定参数的类型,比如p。
某Lambda表达式:
p -> p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 252.The arrow token, ->
右箭头
3. A body, which consists of a singleexpression or a statement block
比如上面的
p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25如果指定一个表达式,Java runtime计算该表达式然后返回其值。
还有另外一种定义法,使用一个return语句。
p -> { return p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25;}return语句不是表达式,在lambda表达式中,你必须使用{}把他括起来。不过你不用将一个返回值为void的方法括起来,比如:
email -> System.out.println(email)
lambda表达式看起来很像一个方法声明。你可以把lambda表达式当做一个匿名方法(没有名字的方法)。下面这个例子Calculator中,lambda表达式看起来不仅仅是一个形式参数。
public class Calculator { interface IntegerMath { int operation(int a, int b); } public int operateBinary(int a, int b, IntegerMath op) { return op.operation(a, b); } public static void main(String... args) { Calculator myApp = new Calculator(); IntegerMath addition = (a, b) -> a + b; IntegerMath subtraction = (a, b) -> a - b; System.out.println("40 + 2 = " + myApp.operateBinary(40, 2, addition)); System.out.println("20 - 10 = " + myApp.operateBinary(20, 10, subtraction)); }}Accessing Local Variables of the Enclosing Scope
import java.util.function.Consumer;public class LambdaScopeTest { public int x = 0; class FirstLevel { public int x = 1; void methodInFirstLevel(int x) { // The following statement causes the compiler to generate // the error "local variables referenced from a lambda expression // must be final or effectively final" in statement A: // // x = 99; Consumer<Integer> myConsumer = (y) -> { System.out.println("x = " + x); // Statement A System.out.println("y = " + y); System.out.println("this.x = " + this.x); System.out.println("LambdaScopeTest.this.x = " + LambdaScopeTest.this.x); }; myConsumer.accept(x); } } public static void main(String... args) { LambdaScopeTest st = new LambdaScopeTest(); LambdaScopeTest.FirstLevel fl = st.new FirstLevel(); fl.methodInFirstLevel(23); }}This example generates the followingoutput:
x = 23
y = 23
this.x = 1
LambdaScopeTest.this.x = 0
1.首先看注释的那些语句,如x=99,和methodInFirstLevel(intx)相冲突,所以会引起编译错误。当然如果将x=99定义成final x =99,可能就没问题了
2.可以看到Lambda表达式中可以通过this.x访问所在类的x属性,还可以通过LambdaScopeTest.this.x访问外部类的x属性。
Target Typing
Lambda表达式类型如何确定?
比如下面的Lambda表达式
p -> p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25在public staticvoid printPersons(List<Person> roster, CheckPerson tester)类型是CheckPerson
在public voidprintPersonsWithPredicate(List<Person> roster, Predicate<Person> tester)类型是Predicate<Person>
也就是说所期望的类型(CheckPerson、Predicate<Person>)叫做target type。
Java编译器可以通过target type的使用上下文或lambda表达式被找到的所在地来决定lambda表达式的类型。
Target Types and Method Arguments
对于方法参数,Java编译器使用其他两种语言特性来决定target type。
这两种语言特性是:1.overload resolution 2.type argument inference
如
public interface Runnable { void run();}public interface Callable<V> { V call();}可以看到两种方法返回值不同,假设我们定义了两个重载方法invoke方法
void invoke(Runnable r) { r.run();}<T> T invoke(Callable<T> c) { return c.call();}那么,如果String s = invoke(() -> "done");调用的是哪一个invoke方法呢?
这个我就不说了,大家自己应该能猜出来。
Serialization
可以序列化一个lambda表达式,但是强烈建议大家不要序列化lambda表达式。
- Java 8中的Lambda表达式(基于Java 8 Tutorial)
- Java 8中的Lambda表达式
- Java 8中的 Lambda表达式
- Java 8 中的 Lambda 表达式
- Java 8中的 Lambda表达式
- Java 8中的 Lambda表达式
- Java 8中的 Lambda表达式
- Java 8 lambda表达式
- Java 8 Lambda表达式
- Java 8 lambda表达式
- Java 8 Lambda 表达式
- Java 8 Lambda表达式
- Java 8 Lambda 表达式
- Java 8 Lambda表达式
- Java 8 Lambda 表达式
- Java 8 Lambda 表达式
- Java 8 Lambda表达式
- Java 8---Lambda表达式
- 二叉树之二叉链表
- K - Yet Another Story of Rock-paper-scissors
- 黑马程序员11--集合
- C++风格的四种类型转换
- 1012.畅通工程
- Java 8中的Lambda表达式(基于Java 8 Tutorial)
- 计算机视觉的一些测试数据集和源码站点
- hbase 0.98.1集群安装
- tiny210(S5PV210)uboot的顶层Makefile的连接命令理解——记tiny210之uboot移植
- 渐进最优
- 英语,贵在坚持!
- Recover Binary Search Tree -- LeetCode
- 图像处理和计算机视觉中的经典论文
- C#与Oracle开发中执行存储过程问题