公子奇带你一步一步了解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 ...
随机推荐
- 突然想起一个有趣的问题:FAT32&NTFS?
在大学的时候老师提过一个有意思的问题4G的程序存储在什么格式以上的电脑? 首先普及一下两种格式的区别 FAT32:此硬盘格式不支持4GB以上大文件,使用32位文件分配表. NTFS:微软最新文件格式, ...
- day1_python之字符串的常用操作
python字符串操作常用操作,如字符串的替换.删除.截取.复制.连接.比较.查找.分割等,需要的朋友可以参考下. 1.去除空格 str.strip():删除字符串两边的指定字符,括号的写入指定字符, ...
- @codeforces - 1186F@ Vus the Cossack and a Graph
目录 @description@ @solution@ @accepted code@ @details@ @description@ 给定一个 n 点 m 边的图(n, m<=10^6),记第 ...
- windows环境下安装nodeJS和express,一直提示command not found-配置环境变量
1.安装NodeJS后,使用npm指令安装express框架,使用 npm install -g express npm install -g express-generator 安装了大半天的时间, ...
- H3C 主机接收IP包
- gradle在build的时候找不到某个jar包的解决办法
前几天公司来新人, 我给他装项目环境的时候遇到一个问题, 在执行gradle build时遇到一系列的错误, 我一个个分析并解决了, 特此记录, 以供他人参考. 一, 首先遇到了找不到spring-b ...
- H3C PPP MP配置示例二
- tensorflow -gpu安装,史上最新最简单的途径(不用自己装cuda,cdnn)
tensorflow -gpu安装首先,安装Anoconda1. 官网下载点我: 2.安装 点击 python 3.6 version自动下载x64版,下载好之后,然后安装. 如图,打上勾之后,一路n ...
- java 利用反射创建对象
创建对象: 1.使用Class对象的newInstance()方法创建该Class对象的实例,此时该Class对象必须要有无参数的构造方法. 2.使用Class对象获取指定的Constructor对象 ...
- 2019-9-24-dotnet-remoting-使用事件
title author date CreateTime categories dotnet remoting 使用事件 lindexi 2019-09-24 15:39:26 +0800 2018- ...