JavaSE 8 :Lambda 快速学习(二)
2014-02-25 22:18
225 查看
使用Lambda表达式改进代码
本章节根据前面的例子来向你展示Lambda表达式是如何改进你的代码的.Lambda表达式更好地支持不要重复自己(Donot
Repeat Yourself)原则,并使你的代码更简单和更具有可读性。
一个常见的查询用例
一个常见的编程用例就是根据特定的规则从数据集合中查找元素。在2012年JavaOne大会上精彩的“Jump-Starting
Lambda”演说中,Stuart Marks 和 MikeDuigou就是使用这个用例做示范。给一个人名单,使用不同规则让机器人和匹配的人通话。这个教程也遵循这一基本前提但稍微有些变化。
在这个示例中,我们的信息需要区分在美国的三个不同的群体。
司机:年龄在16岁以上的
应征者:年龄在18-25岁的男性
飞行员(特指商业飞行员):年龄在23-65之间
能够完成这些任务的真实机器人还没有进入商用阶段.这里不打电话,邮寄或者发送电子邮件,取而代之是将信息在控制台打印出来.信息包含一个人的名字,年龄和一些特定的媒体信息(例如用来发送电邮的电子邮箱地址,打电话所需要的电话号码)
Person Class
在测试名单中的人都是使用Person类来定义并具有以下的属性:
方法创建.下面是该方法的阶段代码片段.注意:这个教程所有的源代码都包含在一个NetBeans工程中,在这一章节的最后有链接地址。
第一次尝试
Pserson类和查询条件都已经定义好,现在你可以写一个RoboContant类。一个可行的解决方法是给每一个用例定义一个方法:
RoboContactsMethods.java
你可以从方法名(callDrivers,
emailDraftees, 和 mailPilots)中明白这些方法描述的都是一种正在发生的行为。只要传达明确的查询条件,机器人根据适当的调用做出动作。可是,这样的设计也有几个不好的地方:
没有遵循“不要重复自己”(DRY)原则
每个方法都重复循环机制
每个方法的选择条件必须重写
针对每个用例好多方法需要实现
代码不灵活。一旦查询条件变化,许多的代码都需要为一个更新而改变。此外,这个代码也不好维护。
重构方法
怎么修改代码呢?从查询条件开始比较好。如果测试条件能够独立放在一个方法里,将会好很多。
RoboContactMethods2.java
查询条件被封装在一个方法里,改善了之前的例子。测试条件可以复用,而且发生变化也不会影响整个类。可是它还有许多代码重复的地方,每个用例还需要一个独立的方法。有没有更好的方式将查询条件传递给这些方法呢?
匿名类
在Lambda表达式之前,匿名内部类是一种选择。例如给一个接口(MyTest.java)定义一个Test方法返回布尔值(函数式接口)是一个可行的解决方法。查询条件当这个方法被调用的时候被传递过去。这个接口就像下面的那样:
更新之后的robot类就像这样的:
RoboContactsAnon.java
这无疑是一种改进的方法,因为只需要三个方法来执行机械手操作。可是,有个小问题就是方法在被调用的时候不优雅。我们看下这个类的测试类:
RoboCallTest03.java
这就是在实际中“垂直”问题的一个很好的例子。这个代码读起来有点困难。此外,我们还必须为每一个用例写自定义的查询条件。
Lambda表达式恰到好处
Lambda表达式能够解决上述所有的问题.
java.util.function
在上一个例子中,MyTest函数式接口将匿名内部类传给方法.可是,不必要再单独写个接口.Java
SE 8提供了java.util.function包里面有许多标准的函数式接口.这个用例中,Predicate接口符合我们的需求。
test方法带有一个泛型类和返回一个boolean值。这正好是条件选择所需要的。下面是robot类的最后版本。
RoboContactsLambda.java
这种途径只需要三个方法,一个为联系方法。Lambda表达式传递给方法来选择符合测试条件的Person实体。
“垂直问题”解决
Lambda表达式解决了垂直问题,任何表达式都能够很容易的复用。再来看一下经过Lambda表达式更改之后新的测试类。
RoboCallTest04.java
注意每一组都设置了Predicate :allDrivers,allDraftees,
和allPilots
。你可以传任意一个Predicate 接口给这些联系方法。代码紧凑很容易读而且没有重复。
本章源码
点击打开链接
本章节根据前面的例子来向你展示Lambda表达式是如何改进你的代码的.Lambda表达式更好地支持不要重复自己(Donot
Repeat Yourself)原则,并使你的代码更简单和更具有可读性。
一个常见的查询用例
一个常见的编程用例就是根据特定的规则从数据集合中查找元素。在2012年JavaOne大会上精彩的“Jump-Starting
Lambda”演说中,Stuart Marks 和 MikeDuigou就是使用这个用例做示范。给一个人名单,使用不同规则让机器人和匹配的人通话。这个教程也遵循这一基本前提但稍微有些变化。
在这个示例中,我们的信息需要区分在美国的三个不同的群体。
司机:年龄在16岁以上的
应征者:年龄在18-25岁的男性
飞行员(特指商业飞行员):年龄在23-65之间
能够完成这些任务的真实机器人还没有进入商用阶段.这里不打电话,邮寄或者发送电子邮件,取而代之是将信息在控制台打印出来.信息包含一个人的名字,年龄和一些特定的媒体信息(例如用来发送电邮的电子邮箱地址,打电话所需要的电话号码)
Person Class
在测试名单中的人都是使用Person类来定义并具有以下的属性:
public class Person { private String givenName; private String surName; private int age; private Gender gender; private String eMail; private String phone; private String address;这个Person类使用Builder来创建新对象.示例的人名单列表是使用createShortList
方法创建.下面是该方法的阶段代码片段.注意:这个教程所有的源代码都包含在一个NetBeans工程中,在这一章节的最后有链接地址。
public static List<Person> createShortList(){ List<Person> people = new ArrayList<>(); people.add( new Person.Builder() .givenName("Bob") .surName("Baker") .age(21) .gender(Gender.MALE) .email("bob.baker@example.com") .phoneNumber("201-121-4678") .address("44 4th St, Smallville, KS 12333") .build() ); people.add( new Person.Builder() .givenName("Jane") .surName("Doe") .age(25) .gender(Gender.FEMALE) .email("jane.doe@example.com") .phoneNumber("202-123-4678") .address("33 3rd St, Smallville, KS 12333") .build() ); people.add( new Person.Builder() .givenName("John") .surName("Doe") .age(25) .gender(Gender.MALE) .email("john.doe@example.com") .phoneNumber("202-123-4678") .address("33 3rd St, Smallville, KS 12333") .build() );
第一次尝试
Pserson类和查询条件都已经定义好,现在你可以写一个RoboContant类。一个可行的解决方法是给每一个用例定义一个方法:
RoboContactsMethods.java
package com.example.lambda; import java.util.List; /** * * @author MikeW */ public class RoboContactMethods { public void callDrivers(List<Person> pl){ for(Person p:pl){ if (p.getAge() >= 16){ roboCall(p); } } } public void emailDraftees(List<Person> pl){ for(Person p:pl){ if (p.getAge() >= 18 && p.getAge() <= 25 && p.getGender() == Gender.MALE){ roboEmail(p); } } } public void mailPilots(List<Person> pl){ for(Person p:pl){ if (p.getAge() >= 23 && p.getAge() <= 65){ roboMail(p); } } } public void roboCall(Person p){ System.out.println("Calling " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getPhone()); } public void roboEmail(Person p){ System.out.println("EMailing " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getEmail()); } public void roboMail(Person p){ System.out.println("Mailing " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getAddress()); } }
你可以从方法名(callDrivers,
emailDraftees, 和 mailPilots)中明白这些方法描述的都是一种正在发生的行为。只要传达明确的查询条件,机器人根据适当的调用做出动作。可是,这样的设计也有几个不好的地方:
没有遵循“不要重复自己”(DRY)原则
每个方法都重复循环机制
每个方法的选择条件必须重写
针对每个用例好多方法需要实现
代码不灵活。一旦查询条件变化,许多的代码都需要为一个更新而改变。此外,这个代码也不好维护。
重构方法
怎么修改代码呢?从查询条件开始比较好。如果测试条件能够独立放在一个方法里,将会好很多。
RoboContactMethods2.java
package com.example.lambda; import java.util.List; /** * * @author MikeW */ public class RoboContactMethods2 { public void callDrivers(List<Person> pl){ for(Person p:pl){ if (isDriver(p)){ roboCall(p); } } } public void emailDraftees(List<Person> pl){ for(Person p:pl){ if (isDraftee(p)){ roboEmail(p); } } } public void mailPilots(List<Person> pl){ for(Person p:pl){ if (isPilot(p)){ roboMail(p); } } } public boolean isDriver(Person p){ return p.getAge() >= 16; } public boolean isDraftee(Person p){ return p.getAge() >= 18 && p.getAge() <= 25 && p.getGender() == Gender.MALE; } public boolean isPilot(Person p){ return p.getAge() >= 23 && p.getAge() <= 65; } public void roboCall(Person p){ System.out.println("Calling " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getPhone()); } public void roboEmail(Person p){ System.out.println("EMailing " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getEmail()); } public void roboMail(Person p){ System.out.println("Mailing " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getAddress()); } }
查询条件被封装在一个方法里,改善了之前的例子。测试条件可以复用,而且发生变化也不会影响整个类。可是它还有许多代码重复的地方,每个用例还需要一个独立的方法。有没有更好的方式将查询条件传递给这些方法呢?
匿名类
在Lambda表达式之前,匿名内部类是一种选择。例如给一个接口(MyTest.java)定义一个Test方法返回布尔值(函数式接口)是一个可行的解决方法。查询条件当这个方法被调用的时候被传递过去。这个接口就像下面的那样:
public interface MyTest<T> { public boolean test(T t); }
更新之后的robot类就像这样的:
RoboContactsAnon.java
public class RoboContactAnon { public void phoneContacts(List<Person> pl, MyTest<Person> aTest){ for(Person p:pl){ if (aTest.test(p)){ roboCall(p); } } } public void emailContacts(List<Person> pl, MyTest<Person> aTest){ for(Person p:pl){ if (aTest.test(p)){ roboEmail(p); } } } public void mailContacts(List<Person> pl, MyTest<Person> aTest){ for(Person p:pl){ if (aTest.test(p)){ roboMail(p); } } } public void roboCall(Person p){ System.out.println("Calling " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getPhone()); } public void roboEmail(Person p){ System.out.println("EMailing " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getEmail()); } public void roboMail(Person p){ System.out.println("Mailing " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getAddress()); } }
这无疑是一种改进的方法,因为只需要三个方法来执行机械手操作。可是,有个小问题就是方法在被调用的时候不优雅。我们看下这个类的测试类:
RoboCallTest03.java
package com.example.lambda; import java.util.List; /** * @author MikeW */ public class RoboCallTest03 { public static void main(String[] args) { List<Person> pl = Person.createShortList(); RoboContactAnon robo = new RoboContactAnon(); System.out.println("\n==== Test 03 ===="); System.out.println("\n=== Calling all Drivers ==="); robo.phoneContacts(pl, new MyTest<Person>(){ @Override public boolean test(Person p){ return p.getAge() >=16; } } ); System.out.println("\n=== Emailing all Draftees ==="); robo.emailContacts(pl, new MyTest<Person>(){ @Override public boolean test(Person p){ return p.getAge() >= 18 && p.getAge() <= 25 && p.getGender() == Gender.MALE; } } ); System.out.println("\n=== Mail all Pilots ==="); robo.mailContacts(pl, new MyTest<Person>(){ @Override public boolean test(Person p){ return p.getAge() >= 23 && p.getAge() <= 65; } } ); } }
这就是在实际中“垂直”问题的一个很好的例子。这个代码读起来有点困难。此外,我们还必须为每一个用例写自定义的查询条件。
Lambda表达式恰到好处
Lambda表达式能够解决上述所有的问题.
java.util.function
在上一个例子中,MyTest函数式接口将匿名内部类传给方法.可是,不必要再单独写个接口.Java
SE 8提供了java.util.function包里面有许多标准的函数式接口.这个用例中,Predicate接口符合我们的需求。
public interface Predicate<T> { public boolean test(T t); }
test方法带有一个泛型类和返回一个boolean值。这正好是条件选择所需要的。下面是robot类的最后版本。
RoboContactsLambda.java
package com.example.lambda; import java.util.List; import java.util.function.Predicate; /** * * @author MikeW */ public class RoboContactLambda { public void phoneContacts(List<Person> pl, Predicate<Person> pred){ for(Person p:pl){ if (pred.test(p)){ roboCall(p); } } } public void emailContacts(List<Person> pl, Predicate<Person> pred){ for(Person p:pl){ if (pred.test(p)){ roboEmail(p); } } } public void mailContacts(List<Person> pl, Predicate<Person> pred){ for(Person p:pl){ if (pred.test(p)){ roboMail(p); } } } public void roboCall(Person p){ System.out.println("Calling " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getPhone()); } public void roboEmail(Person p){ System.out.println("EMailing " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getEmail()); } public void roboMail(Person p){ System.out.println("Mailing " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getAddress()); } }
这种途径只需要三个方法,一个为联系方法。Lambda表达式传递给方法来选择符合测试条件的Person实体。
“垂直问题”解决
Lambda表达式解决了垂直问题,任何表达式都能够很容易的复用。再来看一下经过Lambda表达式更改之后新的测试类。
RoboCallTest04.java
package com.example.lambda; import java.util.List; import java.util.function.Predicate; /** * * @author MikeW */ public class RoboCallTest04 { public static void main(String[] args){ List<Person> pl = Person.createShortList(); RoboContactLambda robo = new RoboContactLambda(); // Predicates Predicate<Person> allDrivers = p -> p.getAge() >= 16; Predicate<Person> allDraftees = p -> p.getAge() >= 18 && p.getAge() <= 25 && p.getGender() == Gender.MALE; Predicate<Person> allPilots = p -> p.getAge() >= 23 && p.getAge() <= 65; System.out.println("\n==== Test 04 ===="); System.out.println("\n=== Calling all Drivers ==="); robo.phoneContacts(pl, allDrivers); System.out.println("\n=== Emailing all Draftees ==="); robo.emailContacts(pl, allDraftees); System.out.println("\n=== Mail all Pilots ==="); robo.mailContacts(pl, allPilots); // Mix and match becomes easy System.out.println("\n=== Mail all Draftees ==="); robo.mailContacts(pl, allDraftees); System.out.println("\n=== Call all Pilots ==="); robo.phoneContacts(pl, allPilots); } }
注意每一组都设置了Predicate :allDrivers,allDraftees,
和allPilots
。你可以传任意一个Predicate 接口给这些联系方法。代码紧凑很容易读而且没有重复。
本章源码
点击打开链接
相关文章推荐
- Java的Url编码和解码
- java UDP 编程简单例子
- Java学习笔记(十一)——通过JDBC操作mysql数据库
- Java序列化算法透析
- java多线程总结
- 【JAVA】JAVAMail使用属性资料
- 【JAVA】通过公式字符串表达式计算值,网上的一种方法
- JavaBean的应用
- Java_Collection Framework集合类详解
- java修改properties文件中的键值对的值
- java打包后的路径问题
- java 之堆实现优先队列
- spring中的web.xml可配置的参数
- spring配置多个xml文件
- java之类型安全的枚举
- Spring Security3.1 最新配置实例 .
- Java编程提高性能时需注意的地方
- 删除的java文件如何找回
- Java中super的几种用法并与this的区别
- Java Web中web.xml文件的作用