在接触Lambda表达式、了解其作用之前,首先来看一下,不用Lambda的时候我们是怎么来做事情的。

我们的需求是,创建一个动物(Animal)的列表,里面有动物的物种名,以及这种动物是否会跳,是否会游泳,并可以根据需要打印出会跳和会游泳的动物名。

首先我们创建一个Animal类:

public class Animal {
private String species;
private boolean canHop;
private boolean canSwim;
private boolean canFly; public Animal(String speciesName, boolean hopper, boolean swimmer, boolean flyer) {
species = speciesName;
canHop = hopper;
canSwim = swimmer;
canFly = flyer;
} public boolean canHop() {
return canHop;
} public boolean canSwim() {
return canSwim;
} public boolean canFly() {
return canFly;
} public String toString() {
return species;
}
}

这个Animal类有四个私有的实例变量和三个共公有的方法。

变量

字符串型的species,用来记录动物的物种名,布尔型的canHopcanSwimcanFly,用来记录这种动物是否能跳,是否能游泳,是否会飞。

构造方法实例化对象的时候会给这些变量赋值。

方法

前三个是返回值为布尔型的canHop()canSwim()canFly(),调用方法时返回canHopcanSwimcanFly变量的值

另外还有一个重写的toString()方法,用于在程序中识别动物的物种名。

下面这段代码创建了一个ArrayList并向List内添加了五个Animal对象,对应五种动物和各自的能力:

List<Animal> animals = new ArrayList<Animal>();
animals.add(new Animal("fish", false, true, false));
animals.add(new Animal("kangroo", true, false, false));
animals.add(new Animal("rabbit", true, false, false));
animals.add(new Animal("turtle", false, true, false));
animals.add(new Animal("bird", true, true, true));

为了判断某种动物是否会跳跃或者会游泳(会不会飞先不管),我们可以写两个方法来返回Animal类的canHop()canSwim(),即获取到了canHopcanSwim私有变量:

public boolean checkIfHopper(Animal a) {
return a.canHop();
} public boolean checkIfSwimmer(Animal a) {
return a.canSwim();
}

在此为了说明Lambda表达式的作用,在这里我们把这两个方法分别放到两个类中,并把方法名改为test

不要问为什么起名叫test而不是Test或者TEST,也不要问为什么一定要把两个方法拆分到两个类里面,照做就是了,后面会说

public class CheckIfHopper {
public boolean test(Animal a) {
return a.canHop();
}
} public class CheckIfSwimmer {
public boolean test(Animal a) {
return a.canSwim();
}
}

一个java文件里只能有一个public class,这里是为了方便所以把多个public class放在一段代码里的,下同

为了校验这两种能力,我们分别创建了一个类,里面分别只有一个方法。即一个类校验一种能力。

按照这个思路,我们可以创建一个名为CheckTraits的接口,来让上面这些类来实现这个接口:

public interface CheckTraits {
boolean test(Animal a);
} public class CheckIfHopper implements CheckTraits {
public boolean test(Animal a) {
return a.canHop();
}
} public class CheckIfSwimmer implements CheckTraits {
public boolean test(Animal a) {
return a.canSwim();
}
}

CheckTraits接口有且只有一个方法boolean test(Animal a)。该方法接受一个Animal类型的对象,并返回布尔值。

有了校验能力的方法,接下来我们只需要去按需要调用这些方法就好了。我们来写一个print方法,接受一个动物列表animals和一个校验种类checker作为参数,打印出列表内符合要求的动物:

private static void print(List<Animal> animals, CheckTraits checker) {
for (Animal animal : animals) {
if (checker.test(animal)) {
System.out.print(animal + " ");
}
System.out.println();
}
}

完整的TraditionalSearch.java代码如下:

import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate; public class TraditionalSearch {
public static void main(String[] args) {
List<Animal> animals = new ArrayList<Animal>();
animals.add(new Animal("fish", false, true, false));
animals.add(new Animal("kangroo", true, false, false));
animals.add(new Animal("rabbit", true, false, false));
animals.add(new Animal("turtle", false, true, false));
animals.add(new Animal("bird", true, true, true)); print(animals, new CheckIfHopper());
System.out.println("-----");
print(animals, new CheckIfSwimmer());
} private static void print(List<Animal> animals, CheckTraits checker) {
for (Animal animal : animals) {
if (checker.test(animal)) {
System.out.print(animal + " ");
}
System.out.println();
}
}
}

代码输出结果为:


kangroo
rabbit bird
-----
fish turtle
bird

到目前为止,我们

  • 创建了一个Animal类
  • 写了两个校验方法,分别放到了两个类CheckIfHopper、CheckIfSwimmer里面
  • 写了一个接口CheckTraits,让上面的这两个类实现自这个接口
  • 在print方法里调用print(animals, new CheckIfHopper())print(animals, new CheckIfSwimmer())

做了这么多铺垫,所以Lambda表达式到底是什么?

首先思考一个问题,根据上面判断跳和游泳的思路,如果想增加一个是否能飞的判断,需要做什么?

  • 创建一个CheckIfFlyer类
  • 实现CheckTraits接口
  • 设置test()方法返回值为a.canFly(还记得这个test方法吗?这个就是我们校验能力的接口里唯一的一个方法名)
  • 在print方法里调用print(animals, new CheckIfFlyer())

虽然这样做也不会增加几行代码,也就是多创建一个类的事情。但是Lambda表达式可以做到更简单:

  • 在print方法里调用print(ainmals, a -> a.canFly())或者print(ainmals, (Animal a) -> {return a.canFly();))

看到了吗?就是这样,并不需要创建一个新的类,也不需要修改test()方法。

我们来看一下Lambda表达式的结构:

  • 左边的a是Lambda表达式的唯一参数
  • 中间的箭头->用来分隔参数主体
  • 右边的a.canHop()主体,调用唯一方法并返回方法的返回值

上图的结构式Lambda表达式的简洁形式,另外一种更为全面和复杂(并不):

  • 当且仅当有且只有一个参数的时候,括号和参数类型才可以被省略
  • 主体部分只有一条语句的时候,Lambda表达式不但可以让你省掉花括号{ },还可以让你省略掉return和后面的分号;
  • 主体部分有多条语句构成一个语句块的时候,花括号内分号不可省略

现在我们了解了Lambda的形式,那么Lambda具体是怎么发挥作用的呢?这要从我们的print()方法说起:

private static void print(List<Animal> animals, CheckTraits checker)

当我们调用print(ainmals, a -> a.canFly()的时候,我们是将Lambda表达式a -> a.canFly()作为print方法的第二个参数传过去的。

print方法的第二个参数接收类型为CheckTraits的变量,所以Java会尝试将Lambda表达式映射到CheckTraits接口上:

boolean test(Animal a);
  • 因为test方法接收Animal类型的变量,所以Lambda表达式的参数也必须为Animal类型
  • 因为test方法的返回值是布尔型,所以Lambda主体也会返回布尔型的值

所以这里其实可以理解为,把简略形式的a -> a.canFly()映射为了具体形式的(Animal a) -> {return a.canFly();}

这里用到了一种叫延迟执行(Deferred Execution)的概念,所谓延迟执行就是”先定义,再执行“。在调用的时候定义了参数的类型和返回值,等执行到print方法里的时候再去执行主体获得返回值。

关于Lambda还有很多更复杂的应用,比如可以使用Predicate<T>接口来省掉自己写接口的步骤,感兴趣可以进一步详细学习。

参考资料:OCA: Oracle Certified Associate Java SE 8 Programmer I Study Guide

Java中的Lambda表达式简介及应用的更多相关文章

  1. Lambda 表达式,Java中应用Lambda 表达式

    一.Lambda 表达式 简单来说,编程中提到的 lambda 表达式,通常是在需要一个函数,但是又不想费神去命名一个函数的场合下使用,也就是指匿名函数. 链接:知乎 先举一个普通的 Python 例 ...

  2. Java中的Lambda表达式

    Lambda来源于希腊字母入,发音为  /'læmdə/对高数有所了解的人都知道λ用于声明一个数学逻辑系统,表示根据XX的输入参数,会返回某个Y结果.这正是编程语言中函数(方法)的意思.因此Lambd ...

  3. java中使用Lambda表达式的5种语法

    1,标准写法 思考下述情况: String[] arr = {"program", "creek", "is", "a" ...

  4. Java 中的 Lambda 表达式

    Lambda表达式 Lambda 表达式是 JDK1.8 的一个新特性,又称特殊的匿名内部类,可以取代大部分的匿名内部类,语法更简洁,可以写出更优雅的 Java 代码,可以极大地优化代码结构. Lam ...

  5. java中使用lambda表达式

    使用lambda表达式能够使复杂的编写方式变的简单 lambda表达式的语法 (parameters) -> expression 或 (parameters) ->{ statement ...

  6. 理解和运用Java中的Lambda

    前提 回想一下,JDK8是2014年发布正式版的,到现在为(2020-02-08)止已经过去了5年多.JDK8引入的两个比较强大的新特性是Lambda表达式(下文的Lambda特指JDK提供的Lamb ...

  7. Java中的lambda匿名函数使用

    Java中的lambda匿名函数使用 lambda匿名函数的使用是为了满足某些情况下需要临时定义函数,或者事先定义,需要时才使用.在python里面,lambda表达式的表达方式为:lambda 参数 ...

  8. 在Android中使用Java 8的lambda表达式

    作为一名Java开发者,或许你时常因为缺乏闭包而产生许多的困扰.幸运的是:Java's 8th version introduced lambda functions给我们带来了好消息;然而,这咩有什 ...

  9. Android中使用lambda表达式

    lambda 语法简介 视频为本篇播客知识点讲解,建议采用超清模式观看, 欢迎点击订阅我的优酷 如果刚学Android,不知道怎么写点击事件可以跳转,传送门 要想在Android中使用lambda语法 ...

随机推荐

  1. So Good They Can't Ignore You

    总体而言,这本书的作者的观点就是,你只有做好了,才会有兴趣,而不是一开始就找可能并不存在的所谓兴趣——好多人就败在不停地找这么一个根本就不存在的兴趣.这个观点简直就是拯救那些乔布斯的粉丝:Follow ...

  2. cn_sql_server_2012_enterprise_edition_x86_x64_dvd_813295 序列号

    cn_sql_server_2012_enterprise_edition_x86_x64_dvd_813295 序列号 MICROSOFT SQL SERVER 2012 ENTERPRISE CO ...

  3. Android零基础入门第86节:探究Fragment生命周期

    一个Activity可以同时组合多个Fragment,一个Fragment也可被多个Activity 复用.Fragment可以响应自己的输入事件,并拥有自己的生命周期,但它们的生命周期直接被其所属的 ...

  4. Ptypes一个开源轻量级的c++库,包括对一些I/O操作、网络通信、多线程和异常处理的封装

    C++开源项目入门级:Ptypes    Ptypes一个开源轻量级的c++库,包括对一些I/O操作.网络通信.多线程和异常处理的封装.虽然代码有限,包括的内容不少,麻雀虽小,五脏俱全.    提高: ...

  5. springboot 2.x处理404、500等异常

    404错误 404错误是不经过Controller的,所以使用@ControllerAdvice或@RestControllerAdvice无法获取到404错误 springboot2处理404错误的 ...

  6. 在VS如何查看汇编代码

    由于最近不常用,结果导致今天用的时候忘记了,╮(╯▽╰)╭.现在标记一下: 方法如下,先创建一个C++ Project,然后加入上面的代码,在main函数或者其他地方设置断点,注意是Debug版本,否 ...

  7. Linux 桌面玩家指南:20. 把 Linux 系统装入 U 盘打包带走

    特别说明:要在我的随笔后写评论的小伙伴们请注意了,我的博客开启了 MathJax 数学公式支持,MathJax 使用$标记数学公式的开始和结束.如果某条评论中出现了两个$,MathJax 会将两个$之 ...

  8. Qt中实现单例模式(SingleTon),大约有3种办法

    Qt中实现单例模式(SingleTon) 单例模式分为“饥汉”和“饿汉”两种版本,也正是线程安全问题使得原本简单的单例模式变得复杂.由于单例模式很常用,Boost库中有强大的泛型单例实现,我也利用Qt ...

  9. 通过OSG实现对模型的日照模拟

    目录 1. 加载模型 2. 光照 1) 环境反射 2) 漫反射 3) 日照方向 (1) 太阳高度角和太阳方位角 (2) 计算过程 4) 改进实现 3. 阴影 4. 太阳高度角与太阳方位角的计算 1) ...

  10. Docker环境下的前后端分离项目部署与运维(十一)Docker Swarm技术

    Docker Swarm技术 docker swarm技术(之前的docker集群都是在 一个虚拟主机上的,但是如果这个主机挂掉了over了,docker技术就是多个虚拟主机形成一个集群) Swarm ...