公子奇带你一步一步了解Java8中Lambda表达式
在上一篇《公子奇带你一步一步了解Java8中行为参数化》中,我们演示到最后将匿名实现简写为
(Police police) -> "浙江".equals(police.getPoliceNativePlace());
这是一个带有箭头的函数,这种写法在Java8中即为Lambda表达式。那么我们就来好好的讲讲Lambda表达式。
一 、什么是Lambda表达式
首先我们需要知道Lambda表达式时JDK8中提出的编码风格,它可以简洁地表示可作为参数传递的匿名函数的一种方式,也可以理解为匿名实现的一种,关于匿名对象的特征它也是有的,例如:它没有名称,但它有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表。基本语法可以使用以下两种方式表示:
(parameters) -> expression
或
(parameters) -> { statements; }
由上可知Lambda表达式有三个部分:
1、参数列表
2、箭头 “ -> ” 用来将参数和主体连接到一起
3、Lambda主体
以下我们通过一些案例来表示一个有效的Lambda表达式
() -> "ABC"; //没有参数但有返回值ABC,return 关键字自动忽略
() -> {}; //没有参数,方法体不执行任何操作
() -> {return "ABC"}; //没有参数,返回ABC,有大括号,需要显示return
(String s) -> s.length(); //有一个参数,并对参数进行操作
(int x, int y) -> { //有两个参数,并做复杂操作,需要大括号
System.out.println("result:");
System.out.println(x + y);
}
(String s1, String s2) -> s1.compareTo(s2) //对两个参数操作
二、什么是函数式接口
在上一篇文章中,我们最后的Lambda表达式是作为一个参数传递给filter方法的,同时在filter方法中的第二个参数是一个接口Predicate<T>,这个接口在JDK8中我们就将其称为函数式接口,回看这个接口只有一个抽象方法。即函数式接口就是只定义一个抽象方法的接口。同时在JDK8中也使用了注解 @FunctionInterface 将一个接口标注为函数式接口,当然没有添加该注解也可为函数式接口,只是添加后程序在运行时会进行检测,否则会抛出异常。同时为了实现更灵活的操作,接口中可以添加静态方法和默认方法(即JDK8之后,接口中是可以定义方法实现的)。
package com.hz; /**
* 在1.8之前,我们一直强调接口中不可有方法实现
* 1.8之后是可以在接口中定义默认方法和静态方法
*/
public interface InterfaceMethod { default void method1() {
System.out.println("接口的默认方法实现...");
} static void method2() {
System.out.println("接口的静态方法实现...");
} public static void main(String[] args) {
InterfaceMethod.method2(); new InterfaceMethod() {}.method1();
}
} //官方提供的一个函数式接口
package java.util.function; import java.util.Objects; @FunctionalInterface
public interface Predicate<T> {
boolean test(T t); default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
} default Predicate<T> negate() {
return (t) -> !test(t);
} default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
} static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
} //说明:从官网的Predicate接口中我们可以发现除了多了注解和一些方法实现,与我们上一讲自己定义的Predicate接口很类似
三、为什么提出函数式接口
可能到这里你也发现了,既然Lambda表达式已经很简洁的表达了实现,我们为什么还需要引入函数式接口的概念呢?为了简化代码和灵活运用,我们提出了Lambda表达式的概念,Lambda表达式允许你直接以内联的形式为函数式接口的抽象方法提供实现,并把整个表达式作为函数式接口的实例。由此即可理解:Lambda表达式是函数式接口的一种实现。
在JDK8中我们会发现大部分函数式接口都添加了 @FunctionInterface 注解,但我们不能仅仅理解为只有添加了该注解的才为函数式接口,我们应该理解的是只有一个抽象方法的接口才为函数式接口。
四、应用场景
既然Lambda表达式这么好,那么我们应该在哪里去使用呢?下面介绍一些常用的:
1、列表循环操作
package com.hz; import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer; public class LambdaTest {
public static void main(String[] args) {
List<String> ss = Arrays.asList("gong", "zi", "qi", "666"); //打印列表中每个值长度
//---匿名实现
ss.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s.length());
}
}); System.out.println("------------------"); //--遍历取值
for (String s : ss) {
System.out.println(s.length());
} System.out.println("---------"); //--Lambda表达式
ss.forEach((String s) -> System.out.println(s.length()));
}
}
//哪种方式简洁、容易理解很明显
2、事件监听
//监听实现一
Button button = new Button("提交");
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("用户点击了提交按钮");
}
}); //监听实现二
button.addActionListener(e -> {
System.out.println("用户点击了提交按钮");
});
3、函数式接口(回看上一篇文章)
更多应用场景,我们在后续文章中再整理。
五、引出方法引用
我们继续回看上一篇,发现在最后调用表达式是何其的相似
Police police) -> police.getPoliceAge() > 30; System.out.println("---------------"); (Police police) -> "浙江".equals(police.getPoliceNativePlace());
那么在JDK8中还可以再次简化吗?答案是肯定的,这就是方法引用。即
Police :: getPoliceAge;
String :: equals;
方法引用的加入可以让我们重复使用现有的方法定义,并像Lambda一样传递它们。我们同时可理解为方法引用是针对仅仅涉及单一方法的Lambda的语法糖。
那么什么情况下,我们可以及如何构建方法引用?
1、指向静态方法的方法引用。
2、指向任意类型实例方法的方法引用。
3、指向现有对象的实例方法的方法引用。
六、复合Lambda表达式组装
在实际开发中,我们不可能只操作一种或一个Lambda表达式,一个表达式的输出可能会是另一个表达式的输入,两个条件的同时满足(例如上一篇中籍贯为浙江年龄大于30的民警),或多个条件只要有一个合适即命中(例如上一篇中籍贯为浙江或年龄大于30的民警)等。如此我们将其分为3类。
1、比较器复合
继续回到上一篇文章,如何对民警年龄进行排列。
public static void main(String[] args) {
List<Police> polices = Arrays.asList(new Police("P001", "余警官", 27, "浙江"),
new Police("P002", "李警官", 32, "安徽"),
new Police("P003", "程警官", 25, "安徽"),
new Police("P004", "杨警官", 35, "浙江"),
new Police("P005", "杨警官", 31, "上海")); polices.sort(comparing(Police :: getPoliceAge).reversed()); System.out.println("结果1: " + polices);
}
2、谓词复合
Predicate<Police> p = (Police police) -> police.getPoliceAge() > 30;
Predicate<Police> p2 = p.and((Police police) -> "浙江".equals(police.getPoliceNativePlace()));
List<Police> result = filter(polices, p2);
System.out.println(result); //当然 除了 and 还有 or方法
3、函数复合
Function<Integer, Integer> f = x -> x + 1;
Function<Integer, Integer> g = x -> x * 2;
Function<Integer, Integer> h = f.andThen(g);
int result = h.apply(1);
System.out.println(result);
七、一个实例
回到上一篇文章场景,将按照一定条件得到的民警按照年龄、籍贯排序。
package com.hz; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate; import static java.util.Comparator.comparing; public class PoliceMain {
public static void main(String[] args) {
List<Police> polices = Arrays.asList(new Police("P001", "余警官", 27, "浙江"),
new Police("P002", "李警官", 32, "安徽"),
new Police("P003", "程警官", 25, "安徽"),
new Police("P004", "杨警官", 35, "浙江"),
new Police("P005", "张警官", 31, "上海"),
new Police("P006", "王警官", 42, "浙江"),
new Police("P007", "赵警官", 31, "浙江"),
new Police("P008", "刘警官", 49, "浙江"),
new Police("P009", "周警官", 32, "浙江")); //问题:找出籍贯为浙江并且年龄大于30岁的民警或者籍贯为安徽的民警,按照民警年龄排序,若年龄相同按照籍贯排序
Predicate<Police> p1 = (Police police) -> police.getPoliceAge() > 30; Predicate<Police> p2 = p1.and((Police police) -> "浙江".equals(police.getPoliceNativePlace())); Predicate<Police> p3 = p2.or((Police police) -> "安徽".equals(police.getPoliceNativePlace())); List<Police> result = filter(polices, p3); result.sort(comparing(Police :: getPoliceAge).thenComparing(Police :: getPoliceNativePlace)); System.out.println("结果: " + result);
} static <T> List<T> filter(List<T> con, Predicate<T> p) {
List<T> result = new ArrayList<>(); for (T e : con) {
if (p.test(e)) {
result.add(e);
}
} return result;
} } // 以上方式需要我们自己去定义一个filter方法 或按照以下方式 package com.hz; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate; import static java.util.Comparator.comparing; public class PoliceMain {
public static void main(String[] args) {
List<Police> polices = Arrays.asList(new Police("P001", "余警官", 27, "浙江"),
new Police("P002", "李警官", 32, "安徽"),
new Police("P003", "程警官", 25, "安徽"),
new Police("P004", "杨警官", 35, "浙江"),
new Police("P005", "张警官", 31, "上海"),
new Police("P006", "王警官", 42, "浙江"),
new Police("P007", "赵警官", 31, "浙江"),
new Police("P008", "刘警官", 49, "浙江"),
new Police("P009", "周警官", 32, "浙江")); //问题:找出籍贯为浙江并且年龄大于30岁的民警或者籍贯为安徽的民警,按照民警年龄排序,若年龄相同按照籍贯排序
Predicate<Police> p1 = (Police police) -> police.getPoliceAge() > 30; Predicate<Police> p2 = p1.and((Police police) -> "浙江".equals(police.getPoliceNativePlace())); Predicate<Police> p3 = p2.or((Police police) -> "安徽".equals(police.getPoliceNativePlace())); Function<List<Police>, List<Police>> f = (List<Police> list) -> {
List<Police> temp = new ArrayList<>();
for (Police police : list) {
if (p3.test(police)) {
temp.add(police);
}
}
return temp;
}; List<Police> result = f.apply(polices); result.sort(comparing(Police :: getPoliceAge).thenComparing(Police :: getPoliceNativePlace)); System.out.println("结果: " + result);
} }
公子奇带你一步一步了解Java8中Lambda表达式的更多相关文章
- 公子奇带你一步一步了解Java8中行为参数化
说明:因为本公子一直从事监狱软件开发,所以本系列博客的引入也以此为背景.问题做了简化,只是为了来讲解技术点. 一.问题提出 今日在好好的撸着代码,超哥(民警)找来了,让把监狱30岁以上的民警找给他. ...
- 公子奇带你进入Java8流的世界(二)
在上一篇中我们带领大家简单的了解流的概念及使用场景,本节我们就来好好的介绍流的常见用法. 一.筛选和切片 对于一串流,我们有时需要取出我们需要的流中某些元素,主要是通过谓词筛选.看代码: 首先定义一个 ...
- 公子奇带你进入Java8流的世界(一)
在说流之前,我们先来看看集合,为什么呢?作为Java8中的新成员,它和集合有很多相似之处,同时它们也是可以互相转化的.集合不仅仅是Java语言,任何一门高级开发语言都有集合的概念,集合顾名思义,就是很 ...
- 3、带你一步一步学习ASP.NET Core中的配置之Configuration
如果你是刚接触ASP.NET Core的学习的话,你会注意到:在ASP.NET Core项目中,看不到.NET Fraemwork时代中的web.config文件和app.config文件了.那么你肯 ...
- C#进阶系列——一步一步封装自己的HtmlHelper组件:BootstrapHelper(三:附源码)
前言:之前的两篇封装了一些基础的表单组件,这篇继续来封装几个基于bootstrap的其他组件.和上篇不同的是,这篇的有几个组件需要某些js文件的支持. 本文原创地址:http://www.cnblog ...
- 【新手出发】从搭虚拟机开始,一步一步在CentOS上跑起来.Net Core程序
文章背景 微软6月26号发布core 1.0版本后,园子里关于这方面的文章就更加火爆了,不管是从文章数量还是大家互动的热情来看,绝对是最热门的技术NO.1.我从去年底开始接触.net core到现在也 ...
- 一步一步学ROP之linux_x64篇
一步一步学ROP之linux_x64篇 一.序 **ROP的全称为Return-oriented programming(返回导向编程),这是一种高级的内存攻击技术可以用来绕过现代操作系统的各种通用防 ...
- 一步一步来做WebQQ机器人-(五)(发送消息||完结)
× 本篇主要是: 发送QQ消息(to:好友,群),以及对小黄鸡抓包利用它的语言库 本文是WebQQ流程的最后一章 最后一章内容不多但我还是啰嗦,可能对大部分人都已知晓的流程方法我也会介绍一下 前面几个 ...
- 一步一步搭框架(asp.netmvc+easyui+sqlserver)-02
一步一步搭框架(asp.netmvc+easyui+sqlserver)-02 我们期望简洁带前台代码,如下: <table id="dataGrid" class=&quo ...
随机推荐
- zip解决杨辉三角问题
杨辉三角原型: / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ 实现: a = [1] while True: print(a) ...
- poj 1039 Pipe (Geometry)
1039 -- Pipe 理解错题意一个晚上._(:з」∠)_ 题意很容易看懂,就是要求你求出从外面射进一根管子的射线,最远可以射到哪里. 正解的做法是,选择上点和下点各一个,然后对于每个折点位置竖直 ...
- xml path 列转行实例
SQL Server2005提供了一个新查询语法——For XML PATH(''),这个语法有什么用呢?想象一下这样一个查询需求:有两个表,班级表A.学生表B,要查询一个班级里有哪些学生?针对这个需 ...
- pytorch JIT浅解析
概要 Torch Script中的核心数据结构是ScriptModule. 它是Torch的nn.Module的类似物,代表整个模型作为子模块树. 与普通模块一样,ScriptModule中的每个单 ...
- 【t065】最敏捷的机器人
Time Limit: 1 second Memory Limit: 128 MB [问题描述] [背景] Wind设计了很多机器人.但是它们都认为自己是最强的,于是,一场比赛开始了~ [问题描述] ...
- 陈志生:德国信贷工厂风控模式对P2P的启发
上海合盘金融信息服务股份有限公司董事长陈志生 和讯银行消息 "2014中国金融论坛"于5月14-15日在北京召开,本次论坛主题为“全面深化金融体制改革与实体经济增长”.和讯网作为指 ...
- React 简书
create-react-app jianshu yarn add styled-components -D 利用js写css样式 样式会更高效 https://github.com ...
- mpvue的坑,持续更新-.-
mpvue... 坑 怎么说呢,去github看一下,发现还是有很多问题没有解决... 不支持filter 亲,到现在还没有支持filter哦.只能用替代方法了,用computed或者渲染前先处理数据 ...
- Codeforces Round #561 (Div. 2)
C. A Tale of Two Lands 题意: 给出 n 个数,问有多少点对(x,y)满足 |x-y| ≤ |x|,|y| ≤ |x+y|: (x,y) 和 (y,x) 表示一种答案: 题解: ...
- P1009 字符三角形
题目描述 输入一个字符c,按照阳历输出的格式输出由该字符组成的一个字符三角形. 输入格式 输入包含一个字符c. 输出格式 输出由该字符c组成的字符三角形. 样例输入 A 样例输出 A AAA AAAA ...