责任链模式的使用

责任链模式(Chain of Responsiblity)

定义: 使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理他为止。
结构实图:

责任链模式涉及角色如下:

* 待处理(Client)角色:一个需要处理的请求,可以是消息或者其他
* 抽象处理者(Handler)角色:定义一个对外处理请求的接口。由它将请求分发到具体处理者( ConcreteHandler)角色进行逐次请求处理。抽象处理者(Handler)角色和具体处理者(ConcreteHandler)角色是聚合关系
* 具体处理者(ConcreteHandler)角色:具体处理者接受到请求时,对其感兴趣的消息处理,或者传递给下个具体处理者处理

SMP中责任链模式的运用

接口定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public interface Filter {

public void messageReceived(String msgType,List<String> contents,NextFliter nextFliter);

public static interface NextFliter{

void doFilter(String msgType,List<String> contents);

}

public static interface FilterChain{
public void addFirst(Filter filter);
public void addLast(Filter filter);
public void messageReceived(String msgType, List<String> contents);
}

}

接口详细说明:

  • Filter接口:

    对应责任链模式中的就是具体处理者(ConcreteHandler)角色,只有一个方法messageReceived(String msgType,List contents,NextFliter nextFliter). 其中msgType和contents就是需要处理的请求消息,对应责任链中的待处理(Client)角色。nextFliter是调用下一个具体处理(ConcreteHandler)角色,这样做的好处就是Filter不必只有下一个Filter的引用。具体如何实现见下面的FilterChain的实现

  • FilterChain接口:

    对应责任链模式中的抽象处理(Handler)者角色。该接口持有所有的Filter,addFirst(Filter filter)和addLast(Filter filter)方法就是向FilterChain中加入具体处理者(ConcreteHandler)角色。当请求消息到来时,FilterChain调用自身的messageReceived方法,将请求传给第一个Filter,Filter再通过自己的messageReceived方法对请求处理或者传递给下一个Filter

  • NextFliter接口:

    从字面意思即下一个Filter,但这个接口同样由FilterChain实现,此接口主要是通知FilterChain获取下一个Filter处理请求消息

FilterChain具体实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class DefaultFilterChain implements Filter.NextFliter,Filter.FilterChain {
private LinkedList<Filter> fliters = new LinkedList<Filter>();
private int index = 0;

public DefaultFilterChain(){}

public void addLast(Filter fliter) {
if (fliter == null)
return;
fliters.addLast(fliter);
}

public void addFirst(Filter fliter) {
if (fliter == null)
return;
fliters.addFirst(fliter);
}


public void messageReceived(String msgType, List<String> contents) {
index = -1;
doFilter(msgType, contents);
}



public void doFilter(String msgType, List<String> contents) {
index++;
if( index < fliters.size()){
fliters.get( index).messageReceived(msgType, contents, this);
}
}

}

FilterChain实现总结:
优点

1. Filters使用链表存储,实现简单。每个Filter不必引用下一个Filter,由FilterChain实现,这样Filter实现比较简单方便
2. FilterChain同时实现NextFliter,每个Filter在调用下一个Filter时只需要调用nextFilter.doFilter(...)方法,这样做实现了接口隔离的效果

不足

1. 内部使用index,造成线程不安全,如果对messageReceived方法同步代价太高,不合适
2. 虽然FilterChain和NextFilter起到接口隔离效果,但两个接口同时由DefaultFilterChain实现,代码耦合度太高,不利于拓展
3. 对请求消息不够抽象,造成功能单一
4. 只能单向过滤处理消息,不能反向

优化空间

1. 不使用LinkedList,自己实现单向链表或者双向链表,避免使用index实现线程安全
2. 使用双向链表实现双向过滤处理,既可以处理messageReceived消息,又可以处理messageSent消息
3. FilterChain和NextFilter分开实现,降低耦合度

有机会介绍下在Mina中责任链是如何实现的…

[译]JavaSE 8 :Lambda 快速学习(四) 完结

Lambda Expressions and Collections

前面的章节已经介绍过Function接口,也已经实现基本的Lambda表达式语法示例.这一章节我们重新回顾Lambda表达式如何通过Collection类来提升.

Lambda Expressions and Collections

到目前所创建的示例中,集合只用到一点.但是一些Lambda表达式新特征改变了集合以往的使用方式.这一章节将介绍这些新特征.

新增类

Drivers,pilotsdraftees查询条件都已经被封装到SearchCriteria
类中
.

SearchCriteria.java

package com.example.lambda;  
 import java.util.HashMap;
 import java.util.Map;
 import java.util.function.Predicate;

 /**
  *
  * @author MikeW
  */
 public class SearchCriteria {

   private final Map<String, Predicate<Person>> searchMap = new HashMap<>();

   private SearchCriteria() {
     super();
     initSearchMap();
   }

   private void initSearchMap() {
     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;

     searchMap.put("allDrivers", allDrivers);
     searchMap.put("allDraftees", allDraftees);
     searchMap.put("allPilots", allPilots);

   }

   public Predicate<Person> getCriteria(String PredicateName) {
     Predicate<Person> target;

     target = searchMap.get(PredicateName);

     if (target == null) {

       System.out.println("Search Criteria not found... ");
       System.exit(1);

     }

     return target;

   }

   public static SearchCriteria getInstance() {
     return new SearchCriteria();
   }
 }

Predicate依赖的查询条件存储在这个类中,对于我们的测试方法也是可以获取到的.

循环

第一个新特性就是对于每个集合类都有一个forEach新方法.下面是打印Person列表的一些示例.

Test01ForEach.java

public class Test01ForEach {

   public static void main(String[] args) {

     List<Person> pl = Person.createShortList();

     System.out.println("\n=== Western Phone List ===");
     pl.forEach( p -> p.printWesternName() );

     System.out.println("\n=== Eastern Phone List ===");
     pl.forEach(Person::printEasternName);

     System.out.println("\n=== Custom Phone List ===");
     pl.forEach(p -> { System.out.println(p.printCustom(r -> "Name: " + r.getGivenName() + " EMail: " + r.getEmail())); });

   }

 }

第一个例子展示了一个标准的Lambda表达式调用printWesternName方法打印出在列表中的每个人。第二个例子演示的是方法引用,在方法已经存在且表示类执行的操作的情况下使用,这种语法可以代替正常的Lambda语法。最后的一个示例展示了printCustom方法在这种情况下也可以使用。请注意当Lambda表达式中包含另一个表达式时的变量名轻微变化。

你可以用这种方式对任何集合进行迭代。基本的结构与增强的for循环相似,但是在类内包含迭代机制带来了许多好处。

链和过滤器

除了循环遍历一个集合的内容,你还可以将这些方法链在一起.第一个方法可以看做是一个过滤器把Predicate接口作为参数.

下面的示例是遍历一个List经过过滤之后的结果集.

Test02Filter.java

public class Test02Filter {
   public static void main(String[] args) {

     List<Person> pl = Person.createShortList();

     SearchCriteria search = SearchCriteria.getInstance();

     System.out.println("\n=== Western Pilot Phone List ===");

     pl.stream().filter(search.getCriteria("allPilots"))
       .forEach(Person::printWesternName);

     System.out.println("\n=== Eastern Draftee Phone List ===");

     pl.stream().filter(search.getCriteria("allDraftees"))
       .forEach(Person::printEasternName);

   }
 }

第一个和最后一个循环演示了List是如何通过查询条件来过滤的.下面是最后一个循环的输出:

=== Eastern Draftee Phone List ===

Name: Baker Bob
Age: 21 Gender: MALE
EMail: bob.baker@example.com
Phone: 201-121-4678
Address: 44 4th St, Smallville, KS 12333

Name: Doe John
Age: 25 Gender: MALE
EMail: john.doe@example.com
Phone: 202-123-4678
Address: 33 3rd St, Smallville, KS 12333

越来越懒

这些特征都很有用,但为何在已经有了for循环的前提下还要把这些特性加入到集合类呢?通过把迭代功能集合到一个库中,它可以让java开发者能做到更好的代码优化.为了更好解释,我们需要了解下面的术语定义:

  • Laziness(惰性):在编程中,惰性是指只处理那些你想要处理的并且你需要处理的对象.在前面的例子中,最后的循环遍历是惰性的因为它只遍历了List过滤后剩余的两个Person对象.这样代码更高效因为最终的处理步骤只发生在被选择的对象上.

  • Eagerness(急切化):List的每个对象上都执行操作的代码被认为是"急切的".例如,一个增强的for循环遍历整个List只为处理两个对象,这样可以认为是更"急切的"途径.

通过把循环集成到Collections库中,当有机会的时候代码可以更好的优化成"惰性"操作."急切的"做法更具有意义的时候(例如计算求和或者平均值),"急切的"操作仍可以使用.这样更高效更灵活相对与总使用"急切"操作而言.

 

stream方法

在前面的代码示例中,我们注意到stream方法在过滤之前和循环开始的时候被调用.这个方法将一个Collection作为输入并返回一个java.util.stream.Stream接口作为输出.一个Stream代表一个元素集合,任何方法都可以链上(个人注解:类似于建造者模式的一种写法,你可以连续调用stream的各种方法)。在默认情况下,一旦元素被消耗它们将不能从stream再次获取到(个人注解:例如调用forEach方法)。因此,一个操作链只能在一个特定的Stream上执行一次。此外,Stream可以串行(默认情况下)或者并行这依赖于方法的调用。在这个章节最后有一个并行处理的Stream示例。

变化和结果

前面提到,一个Stream在使用之后被丢弃。因此,在集合里的元素是不能使用Stream来交换或者改变。但是如果你想保存这些经过链操作之后的元素该怎么办呢?你可以将它们保存到另一个新的集合中。下面的代码将展示如何做到。

Test03toList.java

public class Test03toList {
   public static void main(String[] args) {

     List<Person> pl = Person.createShortList();

     SearchCriteria search = SearchCriteria.getInstance();

     // Make a new list after filtering.
     List<Person> pilotList = pl
             .stream()
             .filter(search.getCriteria("allPilots"))
             .collect(Collectors.toList());

     System.out.println("\n=== Western Pilot Phone List ===");
     pilotList.forEach(Person::printWesternName);

   }

 }

collect方法调用时用到一个参数Collectors类。这个Collectors类能够返回stream的结果的List或者Set。这个例子展示了stream的结果是如何分配到新的List中,并遍历这个List

使用map计算

map方法一般和filter使用.该方法从一个类中获取某一属性并使用它.下面的例子演示如何使用年龄来计算的.

Test04Map.java

public class Test04Map {

   public static void main(String[] args) {
     List<Person> pl = Person.createShortList();

     SearchCriteria search = SearchCriteria.getInstance();

     // Calc average age of pilots old style
     System.out.println("== Calc Old Style ==");
     int sum = 0;
     int count = 0;

     for (Person p:pl){
       if (p.getAge() >= 23 && p.getAge() <= 65 ){
         sum = sum + p.getAge();
         count++;
       }
     }

     long average = sum / count;
     System.out.println("Total Ages: " + sum);
     System.out.println("Average Age: " + average);

     // Get sum of ages
     System.out.println("\n== Calc New Style ==");
     long totalAge = pl
             .stream()
             .filter(search.getCriteria("allPilots"))
             .mapToInt(p -> p.getAge())
             .sum();

     // Get average of ages
     OptionalDouble averageAge = pl
             .parallelStream()
             .filter(search.getCriteria("allPilots"))
             .mapToDouble(p -> p.getAge())
             .average();

     System.out.println("Total Ages: " + totalAge);
     System.out.println("Average Age: " + averageAge.getAsDouble());

   }

 }

这个类的输出如下:

== Calc Old Style ==
Total Ages: 150
Average Age: 37

== Calc New Style ==
Total Ages: 150
Average Age: 37.5

这个程序计算在列表中所有飞行员的平均年龄.第一个循环演示了以前的方式通过for循环来计算.第二个循环使用map方法从串行流中获取每个人的年龄.注意totalAge是个long型。map方法返回一个IntStream对象,它包含sum方法返回long型。

注意:为了第二次计算平均值,不需要再计算年龄的总和。这只是为了教学演示如何使用sum方法。

最后的循环从流(Stream)中计算平均年龄。注意parallelStream方法是用来获取一个并行流这样就可以并行计算数值了。当然返回值跟上面的例子也有点不同。

资源

点击打开链接

 

总结

     在这个教程中,你已经学习了如何使用:

  • Java中的匿名内部类

  • 在Java SE 8中Lambda表达式代替匿名内部类

  • Lambda表达式的正确语法
  • 使用Predicate接口在列表中进行搜索
  • Function接口来处理对象和处理类型不一致的对象

  • 在Java SE 8中Collections加入的新特征来支持Lambda表达式

    <div>
        作者:qarkly112649 发表于2014/3/6 21:39:48 [原文链接](http://blog.csdn.net/qarkly112649/article/details/20654197)
    </div>
    <div>
    阅读:376 评论:0 [查看评论](http://blog.csdn.net/qarkly112649/article/details/20654197#comments)
    </div>
    

[译]JavaSE 8 :Lambda 快速学习(三)

 java.util.function 包

当然Predicate 不是Java SE8提供的唯一函数式接口。此外还设计了一些标准的接口作为开发者的首要选择.

  • Predicate :将Object的属性作为参数传递
  • Consumer :将Object执行的动作作为参数传递
  • Function :将类型T转换成类型U
  • Supplier :提供泛型T的实例(好像一个工厂模式)

  • UnaryOperator :从T到T的一元操作

  • BinaryOperator :从(T,T)到T的二元操作

此外,这些接口许多都已经有原始版本。这些应该做为你的Lambda表达式良好的起始点。

东方风格的名字和方法引用

当正在编写前面的事例的时候,我决定它将是对Person类来说很灵活的打印系统。一个功能需求就是它既能显示西方风格的名字又能显示东方风格的名字。在西方国家,通常是名字在前,姓氏在后。但在许多东方文化中,一般是姓氏在前,名字在后。

一个旧式风格的例子

下面示例是在没有Lambda支持下如何实现Person打印类。

Person.java

public void printWesternName(){  
    System.out.println("\nName: " + this.getGivenName() + " " + this.getSurName() + "\n" +
              "Age: " + this.getAge() + "  " + "Gender: " + this.getGender() + "\n" +
              "EMail: " + this.getEmail() + "\n" +
              "Phone: " + this.getPhone() + "\n" +
              "Address: " + this.getAddress());
   }

   public void printEasternName(){

     System.out.println("\nName: " + this.getSurName() + " " + this.getGivenName() + "\n" +
              "Age: " + this.getAge() + "  " + "Gender: " + this.getGender() + "\n" +
              "EMail: " + this.getEmail() + "\n" +
              "Phone: " + this.getPhone() + "\n" +
              "Address: " + this.getAddress());
   }

每一种风格都对应一个方法来打印输出一个Person

The Function Interface

Function接口对于这种问题很有用。它只有一个方法apply具备下面的特征:

public R apply(T t){ }

它传递有一个泛型T参数,返回一个泛型R。对于这个示例来说,传递Person类和返回String

。对Person一个更灵活的打印方法应该像下面这样写:

Person.java

   public String printCustom(Function <Person, String> f){
       return f.apply(this);
   }

这个示例相对简单一些。一个Function接口被传递到方法中返回一个String。这个apply方法处理Lambda表达式将决定什么样的Person信息将被返回。

这些Function是如何定义的呢?下面是测试代码将调用前面的方法。

NameTestNew.java

 public class NameTestNew {

   public static void main(String[] args) {

     System.out.println("\n==== NameTestNew02 ===");

     List<Person> list1 = Person.createShortList();

     // Print Custom First Name and e-mail
     System.out.println("===Custom List===");
     for (Person person:list1){
         System.out.println(
             person.printCustom(p -> "Name: " + p.getGivenName() + " EMail: " + p.getEmail())
         );
     }

     // Define Western and Eastern Lambdas

     Function<Person, String> westernStyle = p -> {
       return "\nName: " + p.getGivenName() + " " + p.getSurName() + "\n" +
              "Age: " + p.getAge() + "  " + "Gender: " + p.getGender() + "\n" +
              "EMail: " + p.getEmail() + "\n" +
              "Phone: " + p.getPhone() + "\n" +
              "Address: " + p.getAddress();
     };

     Function<Person, String> easternStyle =  p -> "\nName: " + p.getSurName() + " "
             + p.getGivenName() + "\n" + "Age: " + p.getAge() + "  " +
             "Gender: " + p.getGender() + "\n" +
             "EMail: " + p.getEmail() + "\n" +
             "Phone: " + p.getPhone() + "\n" +
             "Address: " + p.getAddress();

     // Print Western List
     System.out.println("\n===Western List===");
     for (Person person:list1){
         System.out.println(
             person.printCustom(westernStyle)
         );
     }

     // Print Eastern List
     System.out.println("\n===Eastern List===");
     for (Person person:list1){
         System.out.println(
             person.printCustom(easternStyle)
         );
     }

   }
 }

第一个循环只是打印名字和email地址。但是任何合法的表达式都可以传递给printCustom方法。东方和西方的打印风格都是使用Lambda表达式定义并存储在一个变量中。这些变量传递给后两个循环中。这些Lambda表达式可以合并到Map中这样能够更容易的复用它们。Lambda表达式提供了很好的灵活性。

示例输出

下面是程序的示例输出结果

==== NameTestNew02 ===
===Custom List===
Name: Bob EMail: bob.baker@example.com
Name: Jane EMail: jane.doe@example.com
Name: John EMail: john.doe@example.com
Name: James EMail: james.johnson@example.com
Name: Joe EMail: joebob.bailey@example.com
Name: Phil EMail: phil.smith@examp;e.com
Name: Betty EMail: betty.jones@example.com

===Western List===

Name: Bob Baker
Age: 21  Gender: MALE
EMail: bob.baker@example.com
Phone: 201-121-4678
Address: 44 4th St, Smallville, KS 12333

Name: Jane Doe
Age: 25  Gender: FEMALE
EMail: jane.doe@example.com
Phone: 202-123-4678
Address: 33 3rd St, Smallville, KS 12333

Name: John Doe
Age: 25  Gender: MALE
EMail: john.doe@example.com
Phone: 202-123-4678
Address: 33 3rd St, Smallville, KS 12333

Name: James Johnson
Age: 45  Gender: MALE
EMail: james.johnson@example.com
Phone: 333-456-1233
Address: 201 2nd St, New York, NY 12111

Name: Joe Bailey
Age: 67  Gender: MALE
EMail: joebob.bailey@example.com
Phone: 112-111-1111
Address: 111 1st St, Town, CA 11111

Name: Phil Smith
Age: 55  Gender: MALE
EMail: phil.smith@examp;e.com
Phone: 222-33-1234
Address: 22 2nd St, New Park, CO 222333

Name: Betty Jones
Age: 85  Gender: FEMALE
EMail: betty.jones@example.com
Phone: 211-33-1234
Address: 22 4th St, New Park, CO 222333

===Eastern List===

Name: Baker Bob
Age: 21  Gender: MALE
EMail: bob.baker@example.com
Phone: 201-121-4678
Address: 44 4th St, Smallville, KS 12333

Name: Doe Jane
Age: 25  Gender: FEMALE
EMail: jane.doe@example.com
Phone: 202-123-4678
Address: 33 3rd St, Smallville, KS 12333

Name: Doe John
Age: 25  Gender: MALE
EMail: john.doe@example.com
Phone: 202-123-4678
Address: 33 3rd St, Smallville, KS 12333

Name: Johnson James
Age: 45  Gender: MALE
EMail: james.johnson@example.com
Phone: 333-456-1233
Address: 201 2nd St, New York, NY 12111

Name: Bailey Joe
Age: 67  Gender: MALE
EMail: joebob.bailey@example.com
Phone: 112-111-1111
Address: 111 1st St, Town, CA 11111

Name: Smith Phil
Age: 55  Gender: MALE
EMail: phil.smith@examp;e.com
Phone: 222-33-1234
Address: 22 2nd St, New Park, CO 222333

Name: Jones Betty
Age: 85  Gender: FEMALE
EMail: betty.jones@example.com
Phone: 211-33-1234
Address: 22 4th St, New Park, CO 222333

资源链接

点击打开链接

<div>
    作者:qarkly112649 发表于2014/3/5 23:21:26 [原文链接](http://blog.csdn.net/qarkly112649/article/details/20577457)
</div>
<div>
阅读:234 评论:0 [查看评论](http://blog.csdn.net/qarkly112649/article/details/20577457#comments)
</div>

[译]JavaSE 8 :Lambda 快速学习(二)

使用Lambda表达式改进代码

本章节根据前面的例子来向你展示Lambda表达式是如何改进你的代码的.Lambda表达式更好地支持不要重复自己(Donot
Repeat Yourself)
原则,并使你的代码更简单和更具有可读性。

一个常见的查询用例

一个常见的编程用例就是根据特定的规则从数据集合中查找元素。在2012JavaOne大会上精彩的“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);

     }
   }

注意每一组都设置了PredicateallDrivers,allDraftees,
allPilots
你可以传任意一个Predicate 接口给这些联系方法。代码紧凑很容易读而且没有重复。

本章源码

           点击打开链接

<div>
    作者:qarkly112649 发表于2014/2/25 22:18:27 [原文链接](http://blog.csdn.net/qarkly112649/article/details/19935375)
</div>
<div>
阅读:291 评论:0 [查看评论](http://blog.csdn.net/qarkly112649/article/details/19935375#comments)
</div>

[译]JavaSE 8 :Lambda 快速学习(一)

注:此文章是Orcale的Lambda教程,第一次翻译发文,翻译有不对和不妥之处多多包涵,还请及时指正以免贻笑大方。。。。

附上原文地址:点击打开链接

概述

目的

  此教程是介绍在Java SE8中新添加的lambda表达式

建议完成时间

  约一个小时

简介

        Lambda表达式是Java SE8 中新加入并且很重要的新特征,它们通过使用表达式提供了一种明确而简洁的方式来表示一个方法接口.Lambda表达式也改进了Collection库,使其从Collection遍历,过滤和抽取数据变得更加容易.另外,新的并发特征也提升在多核环境下的性能.

   该Orcale示例(原文叫Orcale byExamp简称OBE)是对JavaSE8中lambda表达式的初步介绍.在对功能接口和新的lambda语法讨论之后,又对匿名内部类做了介绍.此外对几个常见的使用模式在使用lambda表达式的和没有使用lambda表达的做了展示.

   下一部分评述一个常见的搜索用例和怎样通过使用包含lambda表达式的java代码来进行改进.另外对在java.util.function包中提供的几个常见的功能接口,如PredicateFunction做了实例展示。

     这些OBE完成了对java集合是如何通过使用lambda表达来更新的回顾。

     这些示例的源码也将提供给你。

硬件和软件需求

    下面是需要的硬件和软件的环境(注:只要支持java8的IDE都可以,个人推荐IntelliJ IDEA)













        JDK8



NetBeans7.4


先决条件

      不打算翻译

背景知识

     在Java中匿名内部类是对那些在应用中可能只会发生一次的类的一种实现。例如,在标准的Swing和JavaFx应用的一些需要对键盘和鼠标事件进行处理的事件处理器。相比于对每一个事件写一个独立的事件处理类,你可以使用如下示例的写法

JButton testButton = new JButton("Test Button");
testButton.addActionListener(new ActionListener(){
    @Override public void actionPerformed(ActionEvent ae){
         System.out.println("Click Detected by Anon Class");
      }
  });

否则,你将要给每一个事件的ActionListener实现独立的类.而这样在需要的地方创建类使得代码比较容易阅读.但这样的代码是不优雅的,因为这么多的代码其实只需要定义一个方法.

函数式接口(Functional Interfaces 也有译作功能性接口,)

如下是定义ActionListener接口的代码

package java.awt.event;
import java.util.EventListener;
public interface ActionListener extends EventListener {

   public void actionPerformed(ActionEvent e);

}

这个ActionLisener就是只有一个方法的接口。在Java
SE8
中,像这样形式的接口称之为“函数式接口”。注:这种类型的接口在之前称之为单个抽象方法类型(SAM

通过匿名内部类来使用函数式接口在java中是很常见的模式。除了EventListener类,像RunnableComparator接口也是同样的使用方式。因此函数式接口都是在使用Lambda表达式中被利用到。

Lambda表达式语法

Lambda表达式能够将五行代码转换到一个单独的语句从而解决匿名内部类的蓬松度问题。这个简单的解决方案解决了由内部类引起的“垂直问题”。

Lambda表达式由三个部分组成

Argument List        Arrow Token        Body

(int x, int y)        ->        x + y

Body部分既可以是一个表达式也可以是语句块.如果是在表达式的形式,Body部分只是被简单的计算然后返回值.如果是语句块的形式,Body部分可以认为像方法体一样,return语句将控制返回给匿名方法的调用者.breakcontinue关键字处在顶层是非法的,但是允许放在循环中.如果Body部分中产生结果,那么在每条控制路径都要返回值或者抛出异常.

看下面的例子

(int x, int y) -> x + y

() -> 42

(String s) -> { System.out.println(s); }

第一个表达式带有两个整形参数,分别是xy然后Body部分使用表达式形式将返回x+y的值.

第二个表达式没有参数,也是用表达式的形式将返回整型42.

第三个表达式带有String参数,使用语句块的形式将向控制台输出这个String,没有返回值.

基本的语法都已经覆盖到了,下面看一些例子.

Lambda Example

下面是之前介绍的例子的一些常见用例

Runnable Lambda

下面是怎么使用Lambda来写Runnable

public class RunnableTest {
   public static void main(String[] args) {

      System.out.println("=== RunnableTest ===");

     // Anonymous Runnable
     Runnable r1 = new Runnable(){

       @Override
       public void run(){
         System.out.println("Hello world one!");
       }
     };

     // Lambda Runnable
     Runnable r2 = () -> System.out.println("Hello world two!");

     // Run em!
     r1.run();
     r2.run();

   }
 }

在上面的用例,要注意都没有传递任何参数也没有返回值.RunnableLambda表达式是使用语句块形式,能够将之前的五行代码转换成一条语句.

Comparator Lambda

JavaComparator类经常在集合的排序中使用.下面的例子是一个由Person构成的ArrayList在基于surName属性来排序.

 public class Person {
   private String givenName;
   private String surName;
   private int age;
   private Gender gender;
   private String eMail;
   private String phone;
   private String address;
}
下面的代码通过使用一个匿名内部类和两个Lambda表达式来运用Comparator
 public class ComparatorTest {

   public static void main(String[] args) {

     List<Person> personList = Person.createShortList();

     // Sort with Inner Class
     Collections.sort(personList, new Comparator<Person>(){
       public int compare(Person p1, Person p2){
         return p1.getSurName().compareTo(p2.getSurName());
       }
     });

     System.out.println("=== Sorted Asc SurName ===");
     for(Person p:personList){
       p.printName();
     }

     // Use Lambda instead

     // Print Asc
     System.out.println("=== Sorted Asc SurName ===");
     Collections.sort(personList, (Person p1, Person p2) -> p1.getSurName().compareTo(p2.getSurName()));

     for(Person p:personList){
       p.printName();
     }

     // Print Desc
     System.out.println("=== Sorted Desc SurName ===");
     Collections.sort(personList, (p1,  p2) -> p2.getSurName().compareTo(p1.getSurName()));

     for(Person p:personList){
       p.printName();
     }

   }

 }

17-21行的代码可以很容易的被32行的Lambda表达式代替.我们注意到第一个Lambda表达式声明了参数类型然后传给表达式,可是正如你看到的第二个表达式一样,这是个可选的写法(也可以不写参数的类型).Lambda支持"目标类型匹配"(原文叫target
typing
)它将从在上下文中被使用的对象类型中推导. 因为我们需要给用通用定义的Comparator指定一个类型,编译器可以推导出这两个参数是Person类型的.

Listener Lambda

最后让我们再来回顾下这个ActionListener 例子

 public class ListenerTest {
   public static void main(String[] args) {

     JButton testButton = new JButton("Test Button");
     testButton.addActionListener(new ActionListener(){
     @Override public void actionPerformed(ActionEvent ae){
         System.out.println("Click Detected by Anon Class");
       }
     });

     testButton.addActionListener(e -> System.out.println("Click Detected by Lambda Listner"));

     // Swing stuff
     JFrame frame = new JFrame("Listener Test");
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     frame.add(testButton, BorderLayout.CENTER);
     frame.pack();
     frame.setVisible(true);

   }
 }

注意这个Lambda表达式作为一个参数传递的,"目标类型匹配"(Target
typing)
可以在包括下面的一些上下文中使用:

变量声明

Assignments

返回语句

数组初始化

方法或者构造函数的参数

  • Lambda表示体

条件表达式 ?:

Cast expressions

源码

 点击打开链接

未完,待续。。。。

<div>
    作者:qarkly112649 发表于2014/2/19 22:43:51 [原文链接](http://blog.csdn.net/qarkly112649/article/details/19506343)
</div>
<div>
阅读:557 评论:5 [查看评论](http://blog.csdn.net/qarkly112649/article/details/19506343#comments)
</div>