资讯专栏INFORMATION COLUMN

企业级lambda表达式,让你对lambda有更好的理解

justCoding / 2738人阅读

摘要:但,如果加入了函数式编程,也就是将方法作为形参传递,这必然让开发者为难。但是,其他语言早就使用了函数式编程,比如最常见脚本语言。这就是函数式编程,传递的是一个函数直到,才引用了函数式编程,也就是我们所说的表达式。

导读

Java从jdk1发展到今天,方法的形参类型可以是基本变量,可以是jdk自带的类型,也可以是用户自定义的类型,但是,方法能不能作为形参来传递?我们希望java能够像其他编程语言,可以采用函数式编程思想,换句话说,是将方法当做对象来看,但其在jdk1.8之前都没有这样做。

Java为什么在jdk1.8之前没有这样做?正如我们所知道的,Java自面世以来,便以对象的方式立足。什么都是对象, 对象以方法传递消息,以属性存储数据。其又封装了太多的底层操作,比如说像C++的指针。因而,学习Java的人,感觉到Java简单,这也和Java的初衷相似。

但,如果加入了函数式编程,也就是将方法作为形参传递,这必然让Java开发者为难。但是,其他语言早就使用了函数式编程,比如最常见JavaScript脚本语言。其就可以使用函数式编程,如下代码所示:

</>复制代码

  1. var intArr = [1, 2, 3, 4, 5];
  2. //这就是函数式编程,传递的是一个函数
  3. var newArr = intArr.map(function (item) {
  4. return item * item;
  5. })
  6. alert(newArr);

又因为return item * item;只是一条语句,就像是if当中的单语句可以省略大括号,这里也可以省略大括号,如下代码;

</>复制代码

  1. var newArr = intArr.map(item=> item * item)

但是,如果函数包含多条语句,我们不能这样省略,还是上面的代码。但是,条件变了,如果是偶数的话,同时,结果大于10,我们才输出来。

</>复制代码

  1. var intArr = [1, 2, 3, 4, 5];
  2. //这就是函数式编程,传递的是一个函数
  3. var newArr = intArr.map(function (item) {
  4. var res=0;
  5. if (item % 2 == 0) {
  6. res = item * item;
  7. if (res >= 10) {
  8. return res;
  9. }
  10. }
  11. })
  12. alert(newArr);

直到jdk8,Java才引用了函数式编程,也就是我们所说的lambda表达式。其所对应希腊字母的λ,读音为拉姆达。λ在数学中即表示参数,比如λ矩阵表达式。它也可以是一个表达式,也可以表示一个函数。因而,用它来命名是最为合适的。因为,函数式编程可以讲方法作为形参使用。

Comparator接口

在讲解lambda表达式前,我们先说说Comparator接口,这个想必学Java的都不陌生。它是jdk1.2之后引用的,不过,在jdk1.8的时候,上面加了个注解,如代码所示:

</>复制代码

  1. @FunctionalInterface
  2. public interface Comparator {
  3. 。。。
  4. }

这个注解是什么意思?我们拆开来看,Functional Interface函数式接口,只包含一个方法的接口。函数式接口有什么用?我们在后文再说。

我们经常以方法内部类,来使用该接口,做容器对象的排序所用。

比如,我们现在有一个项目人员类,如代码所示:

</>复制代码

  1. /**
  2. * Created By zby on 22:39 2019/3/20
  3. */
  4. @AllArgsConstructor
  5. @Data
  6. @NoArgsConstructor
  7. @EqualsAndHashCode
  8. @ToString
  9. public class ProjectPerson {
  10. /**
  11. * 年龄
  12. */
  13. private int age;
  14. /**
  15. * 姓名
  16. */
  17. private String name;
  18. /**
  19. * 排序
  20. */
  21. private int sort;
  22. }

我们使用hibernate框架获取人员对象的集合后,像根据人员的年龄进行排序,如代码所示:

</>复制代码

  1. public static void main(String[] args) {
  2. ProjectPerson person1 = new ProjectPerson(23, "zhubaoya1", 0);
  3. ProjectPerson person2 = new ProjectPerson(33, "zhubaoya2", 1);
  4. ProjectPerson person3 = new ProjectPerson(18, "zhubaoya3", 2);
  5. ProjectPerson person4 = new ProjectPerson(17, "zhubaoya4", 3);
  6. ProjectPerson person5 = new ProjectPerson(43, "zhubaoya5", 4);
  7. ProjectPerson person6 = new ProjectPerson(35, "zhubaoya6", 5);
  8. ProjectPerson[] people = new ProjectPerson[6];
  9. people[0] = person1;
  10. people[1] = person2;
  11. people[2] = person3;
  12. people[3] = person4;
  13. people[4] = person5;
  14. people[5] = person6;
  15. Arrays.sort(people, new Comparator() {
  16. @Override
  17. public int compare(ProjectPerson o1, ProjectPerson o2) {
  18. return o2.getAge() - o1.getAge();
  19. }
  20. });
  21. for (ProjectPerson person : people) {
  22. System.out.println(person);
  23. }
  24. }

按倒序输出结果为:
ProjectPerson{age=43, name="zhubaoya5", sort=4}
ProjectPerson{age=35, name="zhubaoya6", sort=5}
ProjectPerson{age=33, name="zhubaoya2", sort=1}
ProjectPerson{age=23, name="zhubaoya1", sort=0}
ProjectPerson{age=18, name="zhubaoya3", sort=2}
ProjectPerson{age=17, name="zhubaoya4", sort=3}

我们上文也说了,既然其是函数式编程,我们能不能将其像JavaScript那样输出数据呢?当然是可以的,因为,我们在编写的时候,idea就有提示,可以用lambda表达式替代,如图所示:

于是,我们用lambda表达式替代,如代码所示:

</>复制代码

  1. public static void main(String[] args) {
  2. 。。。。。
  3. Arrays.sort(people,Comparator.comparing(ProjectPerson::getAge,(age1,age2)->Integer.compare(age2,age1)));
  4. for (ProjectPerson person : people) {
  5. System.out.println(person);
  6. }
  7. }

对于上面的表达式,我们还可以这样写。但不推荐这样写,因为 Integer.compare(age2,age1);是单语句,可以省略大括号,就像上面那样书写。

</>复制代码

  1. Arrays.sort(people,Comparator.comparing(ProjectPerson::getAge,(age1,age2)->{
  2. return Integer.compare(age2,age1);
  3. }));

其输出的结果和上面的一样,不过,这里使用了lambda表达式,那么,什么是lambda表达式呢?

lambda表达式

通过上面的表达式可以看出,lambda表达式传递的是函数体。我们来看Comparator.comparing这个方法,其内部什么样的呢?

</>复制代码

  1. public static Comparator comparing(
  2. Function keyExtractor,
  3. Comparator keyComparator)
  4. {
  5. Objects.requireNonNull(keyExtractor);
  6. Objects.requireNonNull(keyComparator);
  7. return (Comparator & Serializable)
  8. (c1, c2) -> keyComparator.compare(keyExtractor.apply(c1),
  9. keyExtractor.apply(c2));
  10. }

其有两个参数,一个是函数对象,一个比较器对象。而比较器对象是有 @FunctionalInterface注解,这是jdk1.8才有的。也就是说,不是所有的方法,都可以改成lambda表达式。

如果方法的形参类有@FunctionalInterface接口,我们一般可以向其传递代码块。这是个很神奇的地方,方法中也可以写入方法。正如,我们可以向其传递代码块,正如Comparator.comparing(ProjectPerson::getAge,(age1,age2)->Integer.compare(age2,age1))也就是方法。

lambda表达式一般这样表示 形参 + 箭头-> +方法体 这三部分,即

一个代码块

参数

自由变量的值。这是非参数而且不在代码中定义的变量。

单个对象的Lambda

既然清楚了lambda表达式是怎么表示的,那么,我接下来就举公司的例子:如果某个对象的属性值不为空,我们就对该对象的属性值操作,如以下代码所示:

项目的实体类

</>复制代码

  1. /**
  2. * Created By zby on 21:21 2019/3/21
  3. */
  4. @AllArgsConstructor
  5. @NoArgsConstructor
  6. @Data
  7. public class Project {
  8. /**
  9. * 项目名
  10. */
  11. private String name;
  12. /**
  13. * 项目开始时间
  14. */
  15. private Date startTime;
  16. /**
  17. * 项目结束时间
  18. */
  19. private Date endTime;
  20. }

lambdaUtil类

</>复制代码

  1. /**
  2. * Created By zby on 21:22 2019/3/21
  3. *
  4. * @param value 参数值
  5. * @param function 可以接受任何一个参数,并返回响应的值。
  6. */
  7. public static void ifNotNullThen(T value, Consumer function) {
  8. if (value != null) {
  9. function.accept(value);
  10. }
  11. }

Consumer也有@FunctionalInterface,其也有lambda表达式,如下代码:

</>复制代码

  1. public static void main(String[] args) {
  2. try {
  3. // 使用阿里巴巴的fastjson框架
  4. JSONObject jsonObject = new JSONObject();
  5. jsonObject.put("validateTime", "");
  6. // 使用org.apache.commons的框架
  7. final FastDateFormat ISO_DATE_FORMAT = FastDateFormat.getInstance("yyyy-MM-dd");
  8. String startTime = "2018-12-22";
  9. Project project = new Project("新华城", ISO_DATE_FORMAT.parse(startTime), new Date());
  10. jsonObject.put("name", project.getName());
  11. // 使用lambda表达式,输出项目名称和项目有效期
  12. LambdaUtil.ifNotNullThen(project.getEndTime(), t -> jsonObject.replace("validateTime", startTime + " - " + ISO_DATE_FORMAT.format(t)));
  13. System.out.println(jsonObject);
  14. } catch (ParseException e) {
  15. e.printStackTrace();
  16. }
  17. }

输出结果:
{"validateTime":"2018-12-22 - 2019-03-21","name":"新华城"}

其实,也可以不用lambda表达式,这样写也是可以的:

</>复制代码

  1. Date endTime=project.getEndTime();
  2. if (endTime != null){
  3. jsonObject.replace("validateTime", startTime + " - "
  4. + ISO_DATE_FORMAT.format(endTime));
  5. }

为什么用了lambda表达式呢,因为,lambda表达式可以简化代码,让代码变得不那么冗余。尤其是在遍历集合时,更能显示它的优点。

集合的lambda

lambdaUtil的方法

</>复制代码

  1. /**
  2. * Created By zby on 22:21 2019/3/21
  3. * 过滤集合
  4. *
  5. * @param collection 使用容器的接口,保证了松散耦合
  6. * @param function lambda表达式的代码块,map一个代码块,以集合的方式返回
  7. */
  8. public static List simpleMap(Collection collection, Function function) {
  9. if (collection != null && collection.size() > 0) {
  10. return collection.stream().map(function).collect(Collectors.toList());
  11. }
  12. return null;
  13. }

使用上面的过滤方法

</>复制代码

  1. public static void main(String[] args) {
  2. try {
  3. final FastDateFormat ISO_DATE_FORMAT = FastDateFormat.getInstance("yyyy-MM-dd");
  4. String startTime1 = "2018-12-22", endTime1 = "2019-03-12";
  5. String startTime2 = "2018-12-22", endTime2 = "2019-03-14";
  6. String startTime3 = "2018-12-22", endTime3 = "2019-03-16";
  7. Project project1 = new Project("新华城1",
  8. ISO_DATE_FORMAT.parse(startTime1), ISO_DATE_FORMAT.parse(endTime1));
  9. Project project2 = new Project("新华城2",
  10. ISO_DATE_FORMAT.parse(startTime2), ISO_DATE_FORMAT.parse(endTime2));
  11. Project project3 = new Project("新华城3",
  12. ISO_DATE_FORMAT.parse(startTime3), ISO_DATE_FORMAT.parse(endTime3));
  13. List projects = new ArrayList<>();
  14. projects.add(project1);
  15. projects.add(project2);
  16. projects.add(project3);
  17. // 因为测试所用,上面的一般都是从数据库读出来,封装好的数据,真正的项目中不会这样写。
  18. List jsonObjects = LambdaUtil.simpleMap(projects, t -> {
  19. JSONObject jsonObject = new JSONObject();
  20. jsonObject.put("name", t.getName());
  21. LambdaUtil.ifNotNullThen(t.getStartTime(), x -> jsonObject.put("startTime", ISO_DATE_FORMAT.format(x)));
  22. LambdaUtil.ifNotNullThen(t.getEndTime(), x -> jsonObject.put("endTime",ISO_DATE_FORMAT.format(x)));
  23. jsonObject.put("validateTime", jsonObject.getString("startTime") + " - " + jsonObject.getString("endTime"));
  24. jsonObject.remove("startTime");
  25. jsonObject.remove("endTime");
  26. return jsonObject;
  27. });
  28. System.out.println(jsonObjects);
  29. } catch (ParseException e) {
  30. e.printStackTrace();
  31. }

如果,我们不使用lambda表达式,来书写上面的代码,会变得非常大的臃肿。然而,真正的项目不会这样写,数据一般都从数据库读出来,然后,再用过滤的方法进行过滤,如下面的代码:

</>复制代码

  1. /**
  2. * Created By zby on 20:16 2019/1/2
  3. * 展示列表
  4. */
  5. @RequestMapping(value = "/list/{id}", method = RequestMethod.GET)
  6. public Result listProjectProcess(@PathVariable Long id) {
  7. String[] PRO_JSON = {"processId", "dictCode", "dictValue", "opTime", "isChecked"};
  8. List projectProcessDataList = projectProcessService.listProjectProcess(id).getResultData();
  9. List process = simpleMap(projectProcessDataList.subList(0, projectProcessDataList.size() - 3), x -> {
  10. JSONObject json = propsFilter(x, PRO_JSON);
  11. ifNotNullThen(x.getOpTime(), t -> json.replace("opTime", t.substring(0, t.indexOf("."))));
  12. return json;
  13. });
  14. Project project = projectService.get(id).getResultData();
  15. JSONObject body = new JSONObject();
  16. body.put("projectProcess", process);
  17. body.put("resultSummary", project.getResultSummary());
  18. return ResultUtil.buildSuccess(body);
  19. }

这才是真正的项目中使用的lambdaUtil,一般,我们会有架构师将操作lambda表达式方法封装成LambdaUtil类,我们只要在里面填充数据即可。

当然,既然是集合,自然就有集合的排序,虽然,我们会在数据库中排好序,再通过框架的list方法,返回出去,但是,对于枚举的排序集合,还是能够用的到的。

排序的lambda

此外,还回头集合排序的LambdaUtil方法,这个一般很少用,知道就可以了。

</>复制代码

  1. public static List simpleSort(Collection collection, Comparator comparator) {
  2. if (isNull(collection)) return null;
  3. return collection.stream().sorted(comparator).collect(toList());
  4. }
总结

知己知彼,百战不殆。往架构师的方向又迈进了一步!!!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/102817.html

相关文章

  • 业级lambda达式你对lambda更好理解

    摘要:但,如果加入了函数式编程,也就是将方法作为形参传递,这必然让开发者为难。但是,其他语言早就使用了函数式编程,比如最常见脚本语言。这就是函数式编程,传递的是一个函数直到,才引用了函数式编程,也就是我们所说的表达式。 导读 Java从jdk1发展到今天,方法的形参类型可以是基本变量,可以是jdk自带的类型,也可以是用户自定义的类型,但是,方法能不能作为形参来传递?我们希望java能够像其他...

    SegmentFault 评论0 收藏0
  • 《Java8实战》-第三章读书笔记(Lambda达式-02)

    摘要:上下文比如,接受它传递的方法的参数,或者接受它的值得局部变量中表达式需要类型称为目标类型。但局部变量必须显示的声明,或实际上就算。换句话说,表达式只能捕获指派给它们的局部变量一次。注捕获实例变量可以被看作捕获最终局部变量。 由于第三章的内容比较多,而且为了让大家更好的了解Lambda表达式的使用,也写了一些相关的实例,可以在Github或者码云上拉取读书笔记的代码进行参考。 类型检查、...

    iflove 评论0 收藏0
  • Java 8Lambda及其在Android 开发中应用

    摘要:由此可以看出,使用可以让你的代码在某些情况下达到何等的简洁。如果没有参数,那么前面的是必须存在的。我们知道中的,而其实就是一个只定义了一个抽象方法的。也就是说,可以访问定义它的那个方法的局部变量。而在里面,还可以访问所谓的局部变量。 上次在盆友圈发了一张照片 showImg(http://chriszou.com/images/lambda_example.png); 上面的两段代码是...

    liuhh 评论0 收藏0
  • Java 8之方法引用和Lambda达式

    摘要:方法引用在之前只能进行值传递,方法是不能传递的。首先方法接受了一个类型的对象,方法是获取所有的文件,是用来存储筛选之后的元素,循环所有获得到的文件数组,然后调用中的方法来进行条件筛选,放入后返回。 方法引用: 在Java 8之前只能进行值传递,方法是不能传递的。如果你想调用一个方法你必须先获取到它所在的类的实例,然后再通过实例去调用这个方法,但是Java 8新增了方法引用这个新特性可以...

    legendmohe 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<