jdk8新特性之lambda expressions

时间:2020-12-03 19:26:58

本文分两部分:

  1. 语法简单说明
  2. lambda的使用

注:这两部分内容均以类+注释的方式进行说明,并且内容均来自官方教程(https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html)。

第一部分:

/**
* 语法说明类
*
* lambda表达式包含下面几个要素:
* 1、逗号分隔的参数列表,如CheckPerson。test(Person p),其中p表示一个Person的对象实例
* 2、向右箭头 →, 箭头左侧是表达式入参,箭头右侧是表达式本身
* 3、表达式体,包含单个表达式或者一个语句块
*
* @author zhuotao
*
*/
public class SyntaxInstruction {

  public static void main(String[] args) {

List<Person> persons = PersonGenerator.generatePerons();
  printPerson(persons, p -> p.getAge() > 30 && p.getAge() < 50);
}

public static void printPerson(List<Person> persons, CheckPerson cp) {
  for(Person p : persons) {
    if(cp.test(p)) System.out.println(p);
  }
}
}

interface CheckPerson{
  public boolean test(Person p);
}

第二部分:

假设一种场景:
A正在开发一个社交网络应用,考虑添加一个功能——管理员可以根据用户(Person)的不同特征,执行不同的操作,比如发送信息。

approaches是A写的不同的实现,从a1 ~ a8,看看每个方法较之前有什么明显的提升。

备注:1> Person 社交网络应用用户类  2> PersonGenerator 用户列表模拟产生类

方法一:

/**
* 方法一:创建一个方法,用于查询某个特征的用户
* 1、方法入参:
* persons 用户列表
* age 约束条件,最低年龄
* 2、输出打印满足条件的用户信息
*
* 不过,总感觉有什么不对,如果有一天功能的约束条件增加该怎么办?比如,“同事满足age<50”的条件
* 所以,写了第二个方法,见a2.Approch2
*
* @author zhuotao
*
*/
public class Approch1 {

  public static void main(String[] args) {
    List<Person> persons = PersonGenerator.generatePerons();
    printPersonOlderThan(persons, 30);
  }

  public static void printPersonOlderThan(List<Person> persons, int age) {
     for(Person p : persons) {
      if(p.getAge() > age) {
        p.printPerson();
      }
     }
  }

}

方法二:

/**
* 方法二:增强方法的限定规则,判断年龄时,用区间进行判断
* 额,这跟第一种方法就是换汤不换药嘛,没什么改进哈~Σ( ° △ °|||)︴
*
* 于是,第三种方案应运而生。见 a3/Approach3
*
* @author zhuotao
*
*/
public class Approach2 {

public static void main(String[] args) {
  List<Person> persons = PersonGenerator.generatePerons();
  printPersonOlderThan(persons, 30, 50);
}

public static void printPersonOlderThan(List<Person> persons, int low, int high) {
  for(Person p : persons) {
    if(p.getAge() > low && p.getAge() < high) {
      p.printPerson();
    }
  }
}

}

方法三:

/**
* 方法三:创建规则判断接口
* 将规则判断独立出业务判断,如果新增规则约束,那么只需要实现CheckPerson,重写test即可
*
* 仔细想,要是规则很多,肿么办?肿么办?
* 先后有100种规则,是不是要创建100种实现?那类的数量。。。~~~~(>_<)~~~~ 此路不通~
*
* 于是,试试第四中方法,见 a4/Approach4
*
* @author zhuotao
*
*/
public class Approach3 {

public static void main(String[] args) {

  List<Person> persons = PersonGenerator.generatePerons();
  printPerson(persons, new SearchPersonsByAage());

}

public static void printPerson(List<Person> persons, CheckPerson check) {
  for(Person p : persons) {
    if(check.test(p)) p.printPerson();
  }
}

}

class SearchPersonsByAage implements CheckPerson {

@Override
public boolean test(Person p) {
  return p.getAge() > 30 && p.getAge() < 50;
}

}

interface CheckPerson {
  public boolean test(Person p);
}

方法四:

/**
* 方法四:使用匿名内部类
* 使用这种方法,再也不用担心规则类的膨胀问题了~哇咔咔~
*
* 可是,这真的就是想要的方案么?蛋然不是啦~~
*
* 来吧,进入今天的主题——lambda expressions, 见a5/Approach5
*
* @author zhuotao
*
*/
public class Approach4 {

  public static void main(String[] args) {

    List<Person> persons = PersonGenerator.generatePerons();
    printPerson(persons, new CheckPerson() {
      @Override
      public boolean test(Person p) {
        return p.getAge() > 30 && p.getAge() < 50;
      }
    });

  }

  public static void printPerson(List<Person> persons, CheckPerson check) {
    for(Person p : persons) {
      if(check.test(p)) p.printPerson();
    }
  }

}

interface CheckPerson {
  public boolean test(Person p);
}

方法五:

/**
* 方法五,使用lambda表达式
* lambda表达式,需要依赖一个功能接口。这里的功能接口,是指只包含一个抽象方法的接口。如,下面的CheckPerson
*
* 这个例子,发生了什么,连匿名内部类都不需要了,(~ o ~)~zZ 也太简洁了吧~
* 同样是规则判断,这里只需要一个功能接口,一个表达式就解决了规则判断的问题。 (*^__^*)
*
* 这就满足了? 还没有结束哦,卡木昂,北鼻 ,随我来,见 a6/Approach6
*
* @author zhuotao
*
*/
public class Approach5 {

  public static void main(String[] args) {

    List<Person> persons = PersonGenerator.generatePerons();
    printPerson(persons, (Person p) -> p.getAge() > 30 && p.getAge() < 50);
    // 等价于
    //printPerson(persons, p -> p.getAge() > 30 && p.getAge() < 50);

  }

  public static void printPerson(List<Person> persons, CheckPerson check) {
    for(Person p : persons) {
      if(check.test(p)) p.printPerson();
    }
  }

}

interface CheckPerson {
  public boolean test(Person p);
}

方法六:

/**
* 方法六:使用通用功能接口+lambda表达式
* 知道方法5的问题在哪里么? 我来告诉你哈~~
* 因~为~那~个~功~能~接~口~不~通~用~,如果我判断Person之外的对象,岂不是~
*
* 是的,将功能进行通用化处理,参考 Predicate<T> T通用的泛型
*
* 该逻辑: printAdmin(amdins, admin -> "xxxxxx".equals(admin.getPrivilegeCode()));
*
* 在这里lambda的优势已经基本上明了——
* 使用功能接口,替换内部类的使用,降低类膨胀风险;
* 表达式语法简单,简洁易懂;
* 减少coding
*
* 以上,只是自己的片面之言,总结未必到位欢迎补充。
*
* 看到这,lambda已经基本结束了,不过,如果继续看下面的优化相信你会有更大的收获,见 a7/Approach7
*
* @author zhuotao
*
*/
public class Approach6 {

  public static void main(String[] args) {

    List<Person> persons = PersonGenerator.generatePerons();
    printPerson(persons, (Person p) -> p.getAge() > 30 && p.getAge() < 50);
    // 等价于
    //printPerson(persons, p -> p.getAge() > 30 && p.getAge() < 50);

    // 下面只是一个示例,用于说明通用功能接口的优势
    // List<Aministrator> amdins = new ArrayList<Aministrator>();
    // printAdmin(amdins, admin -> "xxxxxx".equals(admin.getPrivilegeCode()));

  }

  public static void printPerson(List<Person> persons, Predicate<Person> check) {
    for(Person p : persons) {
      if(check.test(p)) p.printPerson();
    }
  }

  public static void printAdmin(List<Aministrator> admins, Predicate<Aministrator> check) {
    for(Aministrator admin : admins) {
      if(check.test(admin)) admin.printAdmin();
    }
  }

}

class Aministrator {


private String name;
private String id;
private String privilegeCode;
public String getName() {
  return name;
}
public void setName(String name) {
  this.name = name;
}
public String getId() {
  eturn id;
}
public void setId(String id) {
  this.id = id;
}
public String getPrivilegeCode() {
  return privilegeCode;
}
public void setPrivilegeCode(String privilegeCode) {
  this.privilegeCode = privilegeCode;
}

@Override
public String toString() {
  return "Aministrator [name=" + name + ", id=" + id + ", privilegeCode="
    + privilegeCode + "]";
}

public void printAdmin() {
  System.out.println(toString());
}

}

interface Predicate<T> {
  public boolean test(T t);
}

方法七:

/**
* 方法七:怎么将lambda表达式贯穿到整个应用呢?
* 我稀饭这个标题,因为,这表达的是一种技巧,一种思路
*
* 试想,printPerson方法做了什么?1、接收用户列表 2、规则判断 3、输出打印符合条件的用户信息
* 那如果我有相似的逻辑:比如 1、接收用户列表(待处理数据) 2、执行某个规则(lambda表达式) 3、发送用户信息(操作) 该如何操作呢?
* 这里还是以List<Person> 作为待处理数据,进行优化
* 看processPersons(persons, p -> p.getAge() > 30 && p.getAge() < 50, p -> p.printPerson()); 这个逻辑,你会发现;
* 相同的数据,将规则和规则之后的操作进行了分离。
*
* 那啥,是不是很带感,那就继续,见 a8/Approach8
*
* 注意: 这里使用了 java.util.function.Consumer, 没看错,在jdk8中新引入的function包
*
* 不妨看看Consumer的注释信息(除了accept方法,还有个默认方法,感兴趣的同学可以研究下~)
*
* **
* Represents an operation that accepts a single input argument and returns no
* result. Unlike most other functional interfaces, {@code Consumer} is expected
* to operate via side-effects.
*
* <p>This is a <a href="package-summary.html">functional interface</a>
* whose functional method is {@link #accept(Object)}.
*
* @param <T> the type of the input to the operation
*
* @since 1.8
*
* @FunctionalInterface //这里使用了功能接口的标注
public interface Consumer<T> {......}
*
*
* @author zhuotao
*
*/
public class Approach7 {

  public static void main(String[] args) {

    List<Person> persons = PersonGenerator.generatePerons();
    processPersons(persons, p -> p.getAge() > 30 && p.getAge() < 50, p -> p.printPerson());

  }

  public static void processPersons(List<Person> persons, Predicate<Person> check, Consumer<Person> block) {
    for(Person p : persons) {
      if(check.test(p)) block.accept(p);;
    }
  }

}

interface Predicate<T> {
  public boolean test(T t);
}

方法八:

/**
* 方法八:依然是将lambda表达式运用到整个应用
* 整个题目的调调跟方法七很像,其实a8和a7的关系就像是a6和a5的关系,只是用来说明通用性的
*
* 看到下面的逻辑,有什么想说的? 我是想说,简直太神奇了~~喵喵的~~神奇的jdk8~哇咔咔~
*
* 不过,在新事物(Consumer 和 Function)面前是不是感觉有点不适应,那么接下来就再次见证奇迹吧~
* 见~~~~a9/Approach9
*
* 值得注意的是,这里同样使用了jdk8新增的一个类:java.util.function.Function
* 该类注释如下:
* **
* Represents a function that accepts one argument and produces a result.
*
* <p>This is a <a href="package-summary.html">functional interface</a>
* whose functional method is {@link #apply(Object)}.
*
* @param <T> the type of the input to the function
* @param <R> the type of the result of the function
*
* @since 1.8
*
* @author zhuotao
*
*/
public class Approach8 {

  public static void main(String[] args) {

    List<Person> persons = PersonGenerator.generatePerons();
    processElements(
          persons,
          p -> p.getAge() > 30 && p.getAge() < 50,
          p -> p.getName(),
          name -> System.out.println(name));

  }

/**
* 通用的处理逻辑
* @param source 源数据
* @param tester 通用规则校验函数接口
* @param mapper Funtion mapper,具体请参见function源码,接收一个参数,并返回结果
* @param block
*/
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);
  }
}

}

}

interface Predicate<T> {
  public boolean test(T t);
}

方法九:

/**
* 方法九:Stream操作,也就是聚集操作(aggregate operations)
*
* 先看下面的例子,小伙伴惊呆了么? 神奇的jdk8~
*
* 简单来说,下面的处理通过对集合进行了stream处理; stream 就是集合元素的序列,和Collection不同,stream不是用来存储数据的数据结构,它是通过管道(pipeline)从数据源获取数据。
* pipeline是stream操作的序列,比如本例子中的 filter-map-foreach。 换言之,数据仍然是保存在集合中,聚集操作需要数据时实时获取
*
* 题外话:collection 除了扩展stream之外,还新增了spliterator parallelStream两个方法,感兴趣的同学,巴拉巴拉源码或者debug下本示例进行了解吧~
*
* 到这里lambda表达式入门总算结束了,希望这个专题让大家认识了jdk8的新成员——lambda表达式
* (~ o ~)~zZ 好书湖啦~
*
*
* @author zhuotao
*
*/
public class Approach9 {

  public static void main(String[] args) {

    List<Person> persons = PersonGenerator.generatePerons();
    persons
      .stream() // * Returns a sequential {@code Stream} with this collection as its source.
      .filter(p -> p.getAge() > 30 && p.getAge() < 50) //Returns a stream consisting of the elements of this stream that match the given predicate.
      .map(p -> p.getName()) // Returns a stream consisting of the results of applying the given function to the elements of this stream.
      .forEach(name -> System.out.println(name)); // Performs an action for each element of this stream.

  }

}

interface Predicate<T> {
  public boolean test(T t);
}