java8之lambda表达式&方法引用(一)
本文将简单的介绍一下Lambda表达式和方法引用,这也是Java8的重要更新,Lambda表达式和方法引用最主要的功能是为流(专门负责迭代数据的集合)服务.
什么是lambda表达式
可以把lambda表达式理解为简洁的匿名函数.
我们先声明一个函数式接口(函数式接口:就是只有一个抽象方法的接口. lambda表达式和方法引用,只能用在函数式接口上),比较一下lambda表达式和匿名函数
public interface Animal {
void cry();
public static void main(String [] args){
Animal dog = new Animal() {
@Override
public void cry() {
System.out.println("狗: 汪汪叫");
}
};
dog.cry();
Animal cat = () -> System.out.println("猫: 喵喵叫");
cat.cry();
}
}
一个Animal的接口,里面只有一个cry()的抽象方法, 分别用匿名函数和lambda表达式去实现这个接口. 使用lambda表达式的方法非常的简洁,只需要一行.
lambda表达式语法: 参数 -> 具体的实现.
函数式接口的方法叫做函数描述符,lambda表达式的参数和实现必须和函数描述符的参数和返回值一一对应.cry()方法的参数和返回值都没有所以lambda表达式就是 () -> System.out.println("猫: 喵喵叫");
如果实现有多条语句的话,要写在{}中,并且以;结尾.
() -> {
xxx;
yyy;
return "ccc";
}
列举一个高端一点的使用lambda表达式的方法.以Oracle的Emp(员工表)为例.
表结构
public class Emp {
private BigDecimal empno;
private String ename;
private String job;
private BigDecimal mgr;
private Date hiredate;
private Double sal;
private BigDecimal comm;
private BigDecimal deptno;
public BigDecimal getEmpno() {
return empno;
}
public void setEmpno(BigDecimal empno) {
this.empno = empno;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename == null ? null : ename.trim();
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job == null ? null : job.trim();
}
public BigDecimal getMgr() {
return mgr;
}
public void setMgr(BigDecimal mgr) {
this.mgr = mgr;
}
public Date getHiredate() {
return hiredate;
}
public void setHiredate(Date hiredate) {
this.hiredate = hiredate;
}
public Double getSal() {
return sal;
}
public void setSal(Double sal) {
this.sal = sal;
}
public BigDecimal getComm() {
return comm;
}
public void setComm(BigDecimal comm) {
this.comm = comm;
}
public BigDecimal getDeptno() {
return deptno;
}
public void setDeptno(BigDecimal deptno) {
this.deptno = deptno;
}
}
现在我们要写一个方法,过滤所有工资在3000以上的员工(可能有的人可能会想,我直接写sql不得了,费这么多劲干什么,所以我们以下的测试都假设数据是从redis查询出来的.需要手动写过滤条件)
public List<Emp> filter(List<Emp> listEmp){
List<Emp> filterList = new ArrayList<>();
for (Emp emp :listEmp) {
if (emp.getSal()>3000){
filterList.add(emp);
}
}
return filterList;
}
这么写的坏处是条件硬编码,如果光是改工资,我们可以把3000抽取为一个参数,但是如果要将条件改为小于呢,如果过滤的是员工的工作呢.可能新手就会进行复制粘贴改一改条件,但是当重复的代码达到一定的数量时,维护起来就是个灾难.
我们看看Java8提供的函数式编程,可以怎么解决这个方法.(当然使用匿名函数也可以,但是不够简洁).
把变化的的条件抽取出去,变为一个参数Predicate.具体的实现就是实现这个接口的test方法.
public List<Emp> filter1(List<Emp> listEmp, Predicate<Emp> predicate){
List<Emp> filterList = new ArrayList<>();
for (Emp emp :listEmp) {
if (predicate.test(emp)){
filterList.add(emp);
}
}
return filterList;
}
我们利用了java.util.function这个包提供的Predicate接口.这就是一个标准的函数式接口
测试一下我们写的过滤方法,分别按照工资和工作名称进行过滤
List<Emp> filterSalEmp = empService.filter1(listEmp, Emp emp -> emp.getSal() > 3000);
List<Emp> filterJobEmp = empService.filter1(listEmp, Emp emp -> "SALMAN".equals(emp.getJob()));
Predicate接口的方法 boolean test(T t); 返回值是Boolean类型的,参数是任意类型 我们的实现 Emp emp -> emp.getSal() > 3000 参数Emp ,返回Boolean类型的值 emp.getSal() > 3000 完全满足. 可以看到使用函数式接口编程提高了代码的灵活性和可重用性.
其实lambda表达式的类型是可以从上下文中自己推断出来的,也就是说 上面的 lambda的参数 Emp emp 可以不带参数类型.写成下面这样
List<Emp> filterSalEmp = empService.filter1(listEmp, emp -> emp.getSal() > 3000);
List<Emp> filterJobEmp = empService.filter1(listEmp, emp -> "SALMAN".equals(emp.getJob()));
lambda表达式使用局部变量
回到之前的例子:
String catCry = "猫: 喵喵叫";
Animal cat = () -> System.out.println(catCry);
cat.cry();
打印输出:猫: 喵喵叫 lambda表达式可以使用局部变量,但是必须是final类型的或事实上final类型的(不可改变).
<<java8实战>>中的解释:
第一,实例变量和局部变量背后的实现有一
个关键不同。实例变量都存储在堆中,而局部变量则保存在栈上。如果Lambda可以直接访问局
部变量,而且Lambda是在一个线程中使用的,则使用Lambda的线程,可能会在分配该变量的线
程将这个变量收回之后,去访问该变量。因此,Java在访问自由局部变量时,实际上是在访问它
的副本,而不是访问原始变量。如果局部变量仅仅赋值一次那就没有什么区别了——因此就有了
这个限制。
第二,这一限制不鼓励你使用改变外部变量的典型命令式编程模式(我们会在以后的各章中
解释,这种模式会阻碍很容易做到的并行处理)。
方法引用
方法引用可以理解为lambda表达式的快捷写法,它比lambda表达式更加的简洁,可读性更高.有更好的重用性.如果实现比较简单,一句话就可以实现,复用的地方又不多推荐使用lambda表达式,否则应该使用方法引用.
方法引用的格式 类名::方法名
我们使用方法引用的方式,重新实现上面刚刚过滤员工表的例子.
定义两个条件类,方法的参数和返回值定义的和predicate的函数名描述符一致
public class EmpConditionA {
public static boolean test(Emp emp) {
return emp.getSal() > 3000;
}
}
public class EmpConditionB{
public static boolean test(Emp emp) {
return "engineer".equals(emp.getJob());
}
}
实现方式: 使用类名::方法的方式
List<Emp> listEmp = empService.listEmp();
List<Emp> filterSalEmp = empService.filter1(listEmp, EmpConditionA::test);
List<Emp> filterJobEmp = empService.filter1(listEmp, EmpConditionB::test);
因为这个方法调用的是第三方类的方法所以是static的
还有两种调用方式: 一种是直接调用流中的实例的方式,还有一种是调用局部变量的方式.
直接调用流中的实例的方式: 注意下面的Emp::getJob 就相当于集合中每一个emp对象都调用自己的getJob方法.
这个例子是讲将集合转换为流,map()方法可以理解为对集合的每一个元素进行相应的操作,这里就是对每一个emp实例调用getJob方法.最后.collect(Collectors.toList())将流转换为新的list集合(关于流,笔者后面会继续更新相关的博客).
listEmp.stream().map(Emp::getJob).collect(Collectors.toList());
调用局部变量的方式: 创建条件EmpconditionA的实例
EmpConditionA empConditionA = new EmpConditionA();
List<Emp> filterSalEmp = empService.filter1(listEmp, empConditionA::test);
好了关于lambda表达式和方法引用就简单的介绍到这里,
限于篇幅有些地方介绍的不是很详细,如果有疑问欢迎大家随时提问.
java8之lambda表达式&方法引用(一)的更多相关文章
- 函数式接口 & lambda表达式 & 方法引用
拉呱: 终于,学习jdk8的新特性了,初体验带给我的感觉真爽,代码精简的不行,可读性也很好,而且,spring5也是把jdk8的融入到血液里,总之一句话吧,说的打趣一点,学的时候自己难受,学完了写出来 ...
- 黑马Lambda表达式学习 Stream流 函数式接口 Lambda表达式 方法引用
- 黑马方法引用学习 Stream流 函数式接口 Lambda表达式 方法引用
- 黑马函数式接口学习 Stream流 函数式接口 Lambda表达式 方法引用
- 黑马Stream流学习 Stream流 函数式接口 Lambda表达式 方法引用
- JavaSE-29 Java8的Lambda表达式
概念说明 Lambda表达式是Java8提供的新特性,支持将代码块作为方法的参数. Lambda表达式支持使用简洁的代码创建只有一个方法的接口(函数式接口). 只包含一个方法的接口也称为函数式接口. ...
- Java8特性之Lambda、方法引用以及Stream流
Java 8 中的 Streams API 详解:https://www.ibm.com/developerworks/cn/java/j-lo-java8streamapi/ Java笔记——Jav ...
- Java8中Lambda表达式的10个例子
Java8中Lambda表达式的10个例子 例1 用Lambda表达式实现Runnable接口 //Before Java 8: new Thread(new Runnable() { @Overri ...
- JAVA8之lambda表达式具体解释,及stream中的lambda使用
前言: 本人也是学习lambda不久,可能有些地方描写叙述有误,还请大家谅解及指正! lambda表达式具体解释 一.问题 1.什么是lambda表达式? 2.lambda表达式用来干什么的? 3.l ...
随机推荐
- 综述c++
1.背景 C语言作为结构化和模块化的语言,在处理较小规模的程序时,比较得心应手.但是当问题比较复杂,程序的规模较大时,需要高度的抽象和建模时,C语言显得力不从心. 2.应用领域 如果项目中,既要求效率 ...
- BZOJ1901:Dynamic Rankings
浅谈离线分治算法:https://www.cnblogs.com/AKMer/p/10415556.html 题目传送门:https://lydsy.com/JudgeOnline/problem.p ...
- C# winfrom FastReport 变量设计加载
1.源码 DataTable dt5 = new DataTable(); dt5 = SqlHelper.SqlGetDataTable(StrSql, "tbEmpCont") ...
- IDEA编译器如何去掉注释中参数错误的提示
在使用idea的导入别人的项目的时候经常会在方法注释中出现参数错误的提示,这时我们可以参考下面的配置,将方法注释中的参数错误的提示,更新为警告提示~~ 具体使用方法,参考下图~
- C# 不使用Task实现的多线程顺序执行
多线程有很好的并发性即无序性,在某些特殊情况下需要用到多线程然而又要使其具备顺序性,这种时候就有了一个特殊的场景那就是多线程顺序执行,在现在VS2015中Task自带了顺序执行的方法,但在此之前的旧项 ...
- 类型:。net;问题:HQL;结果:HQL: Hibernate查询语言
HQL: Hibernate查询语言 Hibernate配备了一种非常强大的查询语言,这种语言看上去很像SQL.但是不要被语法结构 上的相似所迷惑,HQL是非常有意识的被设计为完全面向对象的查询,它可 ...
- C语言学习笔记--条件编译
C语言中的条件编译的行为类似于 C 语言中的 if…else…,是预编译指示命令,用于控制是否编译某段代码 . 1.条件编译的本质 (1)预编译器根据条件编译指令有选择的删除代码 (2)编译器不知道代 ...
- 用JS实现,图片放大和缩小
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...
- jumpserver跳板机的搭建
搭建的跳板机基于0.3.2,别问我为什么不用0.5版本的,我能说我没有搭建成功么,步骤贼多,功能不完善,不建议生产环境使用 步骤其实很简单: github wiki :https://github.c ...
- Spring注解-TaskScheduler
一.定义配置类 import org.springframework.context.annotation.ComponentScan; import org.springframework.cont ...