公子奇带你一步一步了解Java8中Lambda表达式

上一篇《公子奇带你一步一步了解Java8中行为参数化》中,我们演示到最后将匿名实现简写为

1 (Police police) -> "浙江".equals(police.getPoliceNativePlace());

这是一个带有箭头的函数,这种写法在Java8中即为Lambda表达式。那么我们就来好好的讲讲Lambda表达式。

一 、什么是Lambda表达式

首先我们需要知道Lambda表达式时JDK8中提出的编码风格,它可以简洁地表示可作为参数传递的匿名函数的一种方式,也可以理解为匿名实现的一种,关于匿名对象的特征它也是有的,例如:它没有名称,但它有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表。基本语法可以使用以下两种方式表示:

1 (parameters) -> expression
2 或
3 (parameters) -> { statements; }

由上可知Lambda表达式有三个部分:

1、参数列表

2、箭头 “ -> ” 用来将参数和主体连接到一起

3、Lambda主体

以下我们通过一些案例来表示一个有效的Lambda表达式

1 () -> "ABC";  //没有参数但有返回值ABC,return 关键字自动忽略
2 () -> {};     //没有参数,方法体不执行任何操作
3 () -> {return "ABC"}; //没有参数,返回ABC,有大括号,需要显示return
4 (String s) -> s.length(); //有一个参数,并对参数进行操作
5 (int x, int y) -> {       //有两个参数,并做复杂操作,需要大括号
6     System.out.println("result:");
7     System.out.println(x + y);
8 }
9 (String s1, String s2) -> s1.compareTo(s2) //对两个参数操作

二、什么是函数式接口

上一篇文章中,我们最后的Lambda表达式是作为一个参数传递给filter方法的,同时在filter方法中的第二个参数是一个接口Predicate<T>,这个接口在JDK8中我们就将其称为函数式接口,回看这个接口只有一个抽象方法。即函数式接口就是只定义一个抽象方法的接口。同时在JDK8中也使用了注解 @FunctionInterface 将一个接口标注为函数式接口,当然没有添加该注解也可为函数式接口,只是添加后程序在运行时会进行检测,否则会抛出异常。同时为了实现更灵活的操作,接口中可以添加静态方法和默认方法(即JDK8之后,接口中是可以定义方法实现的)。

 1 package com.hz;
 2
 3 /**
 4  * 在1.8之前,我们一直强调接口中不可有方法实现
 5  * 1.8之后是可以在接口中定义默认方法和静态方法
 6  */
 7 public interface InterfaceMethod {
 8
 9     default void method1() {
10         System.out.println("接口的默认方法实现...");
11     }
12
13     static void method2() {
14         System.out.println("接口的静态方法实现...");
15     }
16
17     public static void main(String[] args) {
18         InterfaceMethod.method2();
19
20         new InterfaceMethod() {}.method1();
21     }
22 }
23
24 //官方提供的一个函数式接口
25 package java.util.function;
26
27 import java.util.Objects;
28
29 @FunctionalInterface
30 public interface Predicate<T> {
31     boolean test(T t);
32
33     default Predicate<T> and(Predicate<? super T> other) {
34         Objects.requireNonNull(other);
35         return (t) -> test(t) && other.test(t);
36     }
37
38     default Predicate<T> negate() {
39         return (t) -> !test(t);
40     }
41
42     default Predicate<T> or(Predicate<? super T> other) {
43         Objects.requireNonNull(other);
44         return (t) -> test(t) || other.test(t);
45     }
46
47     static <T> Predicate<T> isEqual(Object targetRef) {
48         return (null == targetRef)
49                 ? Objects::isNull
50                 : object -> targetRef.equals(object);
51     }
52 }
53
54 //说明:从官网的Predicate接口中我们可以发现除了多了注解和一些方法实现,与我们上一讲自己定义的Predicate接口很类似

三、为什么提出函数式接口

可能到这里你也发现了,既然Lambda表达式已经很简洁的表达了实现,我们为什么还需要引入函数式接口的概念呢?为了简化代码和灵活运用,我们提出了Lambda表达式的概念,Lambda表达式允许你直接以内联的形式为函数式接口的抽象方法提供实现,并把整个表达式作为函数式接口的实例。由此即可理解:Lambda表达式是函数式接口的一种实现。

在JDK8中我们会发现大部分函数式接口都添加了 @FunctionInterface 注解,但我们不能仅仅理解为只有添加了该注解的才为函数式接口,我们应该理解的是只有一个抽象方法的接口才为函数式接口。

四、应用场景

既然Lambda表达式这么好,那么我们应该在哪里去使用呢?下面介绍一些常用的:

1、列表循环操作

 1 package com.hz;
 2
 3 import java.util.Arrays;
 4 import java.util.List;
 5 import java.util.function.Consumer;
 6
 7 public class LambdaTest {
 8     public static void main(String[] args) {
 9         List<String> ss = Arrays.asList("gong", "zi", "qi", "666");
10
11         //打印列表中每个值长度
12         //---匿名实现
13         ss.forEach(new Consumer<String>() {
14             @Override
15             public void accept(String s) {
16                 System.out.println(s.length());
17             }
18         });
19
20         System.out.println("------------------");
21
22         //--遍历取值
23         for (String s : ss) {
24             System.out.println(s.length());
25         }
26
27         System.out.println("---------");
28
29         //--Lambda表达式
30         ss.forEach((String s) -> System.out.println(s.length()));
31     }
32 }
33 //哪种方式简洁、容易理解很明显

2、事件监听

 1 //监听实现一
 2 Button button = new Button("提交");
 3 button.addActionListener(new ActionListener() {
 4     @Override
 5     public void actionPerformed(ActionEvent e) {
 6         System.out.println("用户点击了提交按钮");
 7     }
 8 });
 9
10 //监听实现二
11 button.addActionListener(e -> {
12     System.out.println("用户点击了提交按钮");
13 });

3、函数式接口(回看上一篇文章)

更多应用场景,我们在后续文章中再整理。

五、引出方法引用

我们继续回看上一篇,发现在最后调用表达式是何其的相似

1 Police police) -> police.getPoliceAge() > 30;
2
3 System.out.println("---------------");
4
5 (Police police) -> "浙江".equals(police.getPoliceNativePlace());

那么在JDK8中还可以再次简化吗?答案是肯定的,这就是方法引用。即

1 Police :: getPoliceAge;
2 String :: equals;

方法引用的加入可以让我们重复使用现有的方法定义,并像Lambda一样传递它们。我们同时可理解为方法引用是针对仅仅涉及单一方法的Lambda的语法糖。

那么什么情况下,我们可以及如何构建方法引用?

1、指向静态方法的方法引用。

2、指向任意类型实例方法的方法引用。

3、指向现有对象的实例方法的方法引用。

六、复合Lambda表达式组装

在实际开发中,我们不可能只操作一种或一个Lambda表达式,一个表达式的输出可能会是另一个表达式的输入,两个条件的同时满足(例如上一篇中籍贯为浙江年龄大于30的民警),或多个条件只要有一个合适即命中(例如上一篇中籍贯为浙江或年龄大于30的民警)等。如此我们将其分为3类。

1、比较器复合

继续回到上一篇文章,如何对民警年龄进行排列。

 1 public static void main(String[] args) {
 2     List<Police> polices = Arrays.asList(new Police("P001", "余警官", 27, "浙江"),
 3             new Police("P002", "李警官", 32, "安徽"),
 4             new Police("P003", "程警官", 25, "安徽"),
 5             new Police("P004", "杨警官", 35, "浙江"),
 6             new Police("P005", "杨警官", 31, "上海"));
 7
 8     polices.sort(comparing(Police :: getPoliceAge).reversed());
 9
10     System.out.println("结果1: " + polices);
11 }

2、谓词复合

1 Predicate<Police> p = (Police police) -> police.getPoliceAge() > 30;
2 Predicate<Police> p2 = p.and((Police police) -> "浙江".equals(police.getPoliceNativePlace()));
3 List<Police> result = filter(polices, p2);
4 System.out.println(result);
5
6 //当然 除了 and  还有 or方法

3、函数复合

1 Function<Integer, Integer> f = x -> x + 1;
2 Function<Integer, Integer> g = x -> x * 2;
3 Function<Integer, Integer> h = f.andThen(g);
4 int result = h.apply(1);
5 System.out.println(result);

七、一个实例

回到上一篇文章场景,将按照一定条件得到的民警按照年龄、籍贯排序。

 1 package com.hz;
 2
 3 import java.util.ArrayList;
 4 import java.util.Arrays;
 5 import java.util.List;
 6 import java.util.function.Predicate;
 7
 8 import static java.util.Comparator.comparing;
 9
10 public class PoliceMain {
11     public static void main(String[] args) {
12         List<Police> polices = Arrays.asList(new Police("P001", "余警官", 27, "浙江"),
13                 new Police("P002", "李警官", 32, "安徽"),
14                 new Police("P003", "程警官", 25, "安徽"),
15                 new Police("P004", "杨警官", 35, "浙江"),
16                 new Police("P005", "张警官", 31, "上海"),
17                 new Police("P006", "王警官", 42, "浙江"),
18                 new Police("P007", "赵警官", 31, "浙江"),
19                 new Police("P008", "刘警官", 49, "浙江"),
20                 new Police("P009", "周警官", 32, "浙江"));
21
22         //问题:找出籍贯为浙江并且年龄大于30岁的民警或者籍贯为安徽的民警,按照民警年龄排序,若年龄相同按照籍贯排序
23         Predicate<Police> p1 = (Police police) -> police.getPoliceAge() > 30;
24
25         Predicate<Police> p2 = p1.and((Police police) -> "浙江".equals(police.getPoliceNativePlace()));
26
27         Predicate<Police> p3 = p2.or((Police police) -> "安徽".equals(police.getPoliceNativePlace()));
28
29         List<Police> result = filter(polices, p3);
30
31         result.sort(comparing(Police :: getPoliceAge).thenComparing(Police :: getPoliceNativePlace));
32
33         System.out.println("结果: " + result);
34     }
35
36     static <T> List<T> filter(List<T> con, Predicate<T> p) {
37         List<T> result = new ArrayList<>();
38
39         for (T e : con) {
40             if (p.test(e)) {
41                 result.add(e);
42             }
43         }
44
45         return result;
46     }
47
48 }
49
50 // 以上方式需要我们自己去定义一个filter方法  或按照以下方式
51
52 package com.hz;
53
54 import java.util.ArrayList;
55 import java.util.Arrays;
56 import java.util.List;
57 import java.util.function.Function;
58 import java.util.function.Predicate;
59
60 import static java.util.Comparator.comparing;
61
62 public class PoliceMain {
63     public static void main(String[] args) {
64         List<Police> polices = Arrays.asList(new Police("P001", "余警官", 27, "浙江"),
65                 new Police("P002", "李警官", 32, "安徽"),
66                 new Police("P003", "程警官", 25, "安徽"),
67                 new Police("P004", "杨警官", 35, "浙江"),
68                 new Police("P005", "张警官", 31, "上海"),
69                 new Police("P006", "王警官", 42, "浙江"),
70                 new Police("P007", "赵警官", 31, "浙江"),
71                 new Police("P008", "刘警官", 49, "浙江"),
72                 new Police("P009", "周警官", 32, "浙江"));
73
74         //问题:找出籍贯为浙江并且年龄大于30岁的民警或者籍贯为安徽的民警,按照民警年龄排序,若年龄相同按照籍贯排序
75         Predicate<Police> p1 = (Police police) -> police.getPoliceAge() > 30;
76
77         Predicate<Police> p2 = p1.and((Police police) -> "浙江".equals(police.getPoliceNativePlace()));
78
79         Predicate<Police> p3 = p2.or((Police police) -> "安徽".equals(police.getPoliceNativePlace()));
80
81         Function<List<Police>, List<Police>> f = (List<Police> list) -> {
82             List<Police> temp = new ArrayList<>();
83             for (Police police : list) {
84                 if (p3.test(police)) {
85                     temp.add(police);
86                 }
87             }
88             return temp;
89         };
90
91         List<Police> result = f.apply(polices);
92
93         result.sort(comparing(Police :: getPoliceAge).thenComparing(Police :: getPoliceNativePlace));
94
95         System.out.println("结果: " + result);
96     }
97
98 }

原文地址:https://www.cnblogs.com/chuanqi1415583094/p/12146983.html

时间: 2024-10-11 11:44:36

公子奇带你一步一步了解Java8中Lambda表达式的相关文章

公子奇带你一步一步了解Java8中行为参数化

说明:因为本公子一直从事监狱软件开发,所以本系列博客的引入也以此为背景.问题做了简化,只是为了来讲解技术点. 一.问题提出 今日在好好的撸着代码,超哥(民警)找来了,让把监狱30岁以上的民警找给他. 二.功能实现 这个简单.什么也不用说,代码撸起来.首先定义实体类 package com.hz.pojo; /** * 民警实体类 */ public class Police { /** * 民警警号 */ private String policeNo; /** * 民警姓名 */ privat

公子奇带你进入Java8流的世界(二)

在上一篇中我们带领大家简单的了解流的概念及使用场景,知道了流的本质操作是将外部迭代转为了内部迭代,由此从程序并发性上得到了很大的优化.在本节我们就来好好的介绍流的常见用法. 一.筛选和切片 对于一串流,我们有时需要取出我们需要的流中某些元素,主要是通过谓词筛选.看代码: 首先定义一个POJO,后续操作都基于此来实例化元素. 1 package com.hz; 2 3 /** 4 * 民警实体类 5 */ 6 public class Police { 7 /** 8 * 民警警号 9 */ 10

【转】朱兆祺带你一步一步学习嵌入式(连载)

原文网址:http://bbs.elecfans.com/jishu_357014_2_1.html#comment_top  从最初涉及嵌入式Linux开始到现在,深深的知道嵌入式的每一步学习都是举步维艰.从去年11月份开始,我就着手整理各种学习资料,希望推动嵌入式学习的前进贡献自己微不足道的一份力量.从去年到现在,将C语言的学习经验整理成<攻破C语言笔试与机试陷阱及难点>(现在仍在更新),这份资料已经在电子发烧友论坛的单片机论坛连载(http://bbs.elecfans.com/jish

带你一步一步的解析ARouter 源码

ARouter 是阿里推出的一款页面路由框架.由于项目中采用了组件化架构进行开发,通过 ARouter 实现了页面的跳转,之前看它的源码时忘了写笔记,因此今天来重新对它的源码进行一次分析. (顺手留下GitHub链接,需要获取相关面试或者面试宝典核心笔记PDF等内容的可以自己去找)https://github.com/xiangjiana/Android-MS 本篇源码解析基于 ARouter 1.2.4 初始化 ARouter 在使用前需要通过调用 Arouter.init方法并传入 Appl

面试被问懵?带你一步一步深入Handler源码,不信还拿不下面试官?

Handler机制是Android中相当经典的异步消息机制,在Android发展的历史长河中扮演着很重要的角色,无论是我们直接面对的应用层还是FrameWork层,使用的场景还是相当的多. 很多朋友面试时问到了这里,一时被问懵.从哪里跌倒就从哪里爬起来,带大家一步一步深入Handler源码,就不信还拿不下面试官! BATJ.字节跳动面试专题,算法专题,高端技术专题,混合开发专题,java面试专题,Android,Java小知识,到性能优化.线程.View.OpenCV.NDK等已经上传到了的我的

带大家一步一步封装一个聊天键盘(二)

继上次写了封装聊天键盘(一)地址(http://www.cnblogs.com/bcblogs/p/4704046.html),今天有时间就继续写吧,哈哈,有什么问题可以在评论里发给我哦(红色字体是我当时怎么想的) 上次写到表情键盘,今天我们把余下的写完 首先,我们先把扩展的View写了,扩展的View我是用的自定义的View,其实也就是几张图片而已啦,继承UIView自定义一个View 我的想法是,封装一个view,传入一个数组,就能显示一排间隔相等的图片,我定义的时每一行有4个button,

一步一步跟我学习lucene(19)---lucene增量更新和NRT(near-real-time)Query近实时查询

这两天加班,不能兼顾博客的更新,请大家见谅. 有时候我们创建完索引之后,数据源可能有更新的内容,而我们又想像数据库那样能直接体现在查询中,这里就是我们所说的增量索引.对于这样的需求我们怎么来实现呢?lucene内部是没有提供这种增量索引的实现的: 这里我们一般可能会想到,将之前的索引全部删除,然后进行索引的重建.对于这种做法,如果数据源的条数不是特别大的情况下倒还可以,如果数据源的条数特别大的话,势必会造成查询数据耗时,同时索引的构建也是比较耗时的,几相叠加,势必可能造成查询的时候数据缺失的情况

一步一步跟我学习lucene(9)---lucene搜索之拼写检查和相似度查询提示(spellcheck)

suggest应用场景 用户的输入行为是不确定的,而我们在写程序的时候总是想让用户按照指定的内容或指定格式的内容进行搜索,这里就要进行人工干预用户输入的搜索条件了:我们在用百度谷歌等搜索引擎的时候经常会看到按键放下的时候直接会提示用户是否想搜索某些相关的内容,恰好lucene在开发的时候想到了这一点,lucene提供的suggest包正是用来解决上述问题的. suggest包联想词相关介绍 suggest包提供了lucene的自动补全或者拼写检查的支持: 拼写检查相关的类在org.apache.

loadrunner安装运行一步一步来(多图)

安装loadrunner 一路遇到很多坑,很多坑,坑,为什么呢? 因为这软件是收费的,他操作文档写的很详细,就是不写基础环境的配置,下面安装过程写详细一些,减少大家没必要时间上的浪费和对此的谩骂 现在loadrunner 12的版本已经出来了,不过还没有破解,所以先安装测试11的版本,不绕圈子,先下载, 链接: http://pan.baidu.com/s/1kT8CbVh 密码: v4br 加密码是怕被删 遇到的坑 下面是通用的安装说明: 1.下载loadrunner-11.zip文件,解压缩