本文将简单的介绍一下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表达式&方法引用(一)的更多相关文章

  1. 函数式接口 & lambda表达式 & 方法引用

    拉呱: 终于,学习jdk8的新特性了,初体验带给我的感觉真爽,代码精简的不行,可读性也很好,而且,spring5也是把jdk8的融入到血液里,总之一句话吧,说的打趣一点,学的时候自己难受,学完了写出来 ...

  2. 黑马Lambda表达式学习 Stream流 函数式接口 Lambda表达式 方法引用

  3. 黑马方法引用学习 Stream流 函数式接口 Lambda表达式 方法引用

  4. 黑马函数式接口学习 Stream流 函数式接口 Lambda表达式 方法引用

  5. 黑马Stream流学习 Stream流 函数式接口 Lambda表达式 方法引用

  6. JavaSE-29 Java8的Lambda表达式

    概念说明 Lambda表达式是Java8提供的新特性,支持将代码块作为方法的参数. Lambda表达式支持使用简洁的代码创建只有一个方法的接口(函数式接口). 只包含一个方法的接口也称为函数式接口. ...

  7. Java8特性之Lambda、方法引用以及Stream流

    Java 8 中的 Streams API 详解:https://www.ibm.com/developerworks/cn/java/j-lo-java8streamapi/ Java笔记——Jav ...

  8. Java8中Lambda表达式的10个例子

    Java8中Lambda表达式的10个例子 例1 用Lambda表达式实现Runnable接口 //Before Java 8: new Thread(new Runnable() { @Overri ...

  9. JAVA8之lambda表达式具体解释,及stream中的lambda使用

    前言: 本人也是学习lambda不久,可能有些地方描写叙述有误,还请大家谅解及指正! lambda表达式具体解释 一.问题 1.什么是lambda表达式? 2.lambda表达式用来干什么的? 3.l ...

随机推荐

  1. h5废弃的标签和属性及新增的标签和属性

    一.废弃的标签和属性 1.表现性元素 a) basefont b) big c) center d) font e) strike f) tt 2.框架类元素 a) frame b) frameset ...

  2. HttpContext是干什么的

    这是MSDN对HttpContext的说明:        HttpContext 类:封装有关个别 HTTP 请求的所有 HTTP 特定的信息. (网上说是上下文信息,啥又叫上下文呢?个人感觉说的不 ...

  3. js易犯错误与易混淆的重要知识点

    一:作用域的问题 简单案例1: var a = 1; var n = function () { console.log(a); var a=2; } n(); =>输出undefined原因: ...

  4. MyEclipse启动tomcat增加内存配置

    omcat增加内存在catalina.bat下 MyEclipse增加内存 设置Window->Preferences->Application Servers->Tomcat -- ...

  5. Visualforce入门第二篇_2017.3.1

    代码实现类似Html的表单(Form) <apex:page sidebar="false" standardController="Account"&g ...

  6. 基于dubbo的SOA项目改造

    工程改造: 1.原来工程删除掉表现层的模块,将表现层独立出来. 2.将原来的工程改造.将service的打包方式改为改为war. 3.在service模块中添加web.xml文件 4.在web.xml ...

  7. UITableView常见用法以及注意细节

    UITableView用途广泛,而且多样化,其中有一些不太常用,用到的话需要去查资料,今天边用边记录下来 *第一发:UITableViewCell 分割线 1. 分割线样式 [self.tableVi ...

  8. asp.net过滤HTML标签,只保留换行与空格

    自己从网上找了一个过滤HTML标签的方法,我也不知道谁的才是原创的,反正很多都一样.我把那方法复制下来,代码如下: /// <summary> /// 去除HTML标记 /// </ ...

  9. (转)更改Web.config中对上传文件大小限制

    .net上传超过200K的图片的时候,会跳转到404,但是url没有错误,真J8的坑啊. 本文转载自:http://www.cnblogs.com/zwffff/archive/2009/04/29/ ...

  10. spring容器启动的三种方式

    一.在Web项目中,启动Spring容器的方式有三种,ContextLoaderListener.ContextLoadServlet.ContextLoaderPlugin. 1.1.监听器方式: ...