只要能获取到结果, 谁去做的, 怎么做的都不重要, 重视的是结果, 不重视过程
Lambda 表达式的由来
我们查看下面的代码
- package demo03;
- public class Demo01 {
- public static void main(String[] args) {
- // 匿名内部类的方式创建多线程实现类
- new Thread(new Runnable() {
- @Override
- public void run() {
- System.out.println("当前线程的名称:" + Thread.currentThread().getName());
- }
- }).start();// 启动线程
- }
- }
代码分析
对于 Runnable 的匿名内部类用法, 可以分析出几点内容:
Thread 类需要 Runnable 接口作为参数, 其中的抽象 run 方法是用来指定线程任务内容的核心;
为了指定 run 的方法体, 不得不需要 Runnable 接口的实现类;
为了省去定义一个 RunnableImpl 实现类的麻烦, 不得不使用匿名内部类;
必须覆盖重写抽象 run 方法, 所以方法名称, 方法参数, 方法返回值不得不再写一遍, 且不能写错;
而实际上, 似乎只有方法体才是关键所在.
编程思想转换
做什么, 而不是怎么做
我们真的希望创建一个匿名内部类对象吗? 不. 我们只是为了做这件事情而不得不创建一个对象. 我们真正希望做的事情是: 将 run 方法体内的代码传递给 Thread 类知晓.
传递一段代码 -- 这才是我们真正的目的. 而创建对象只是受限于面向对象语法而不得不采取的一种手段方式. 那, 有没有更加简单的办法? 如果我们将关注点从 "怎么做" 回归到 "做什么" 的本质上, 就会发现只要能够更好地达到目的, 过程与形式其实并不重要.
2014 年 3 月 Oracle 所发布的 Java 8(JDK 1.8)中, 加入了 Lambda 表达式的重量级新特性, 为我们打开了新世界的大门.
同样是实现多线程, 下面我们体验 Lambda 表达式的写法
- package demo03;
- public class Demo01 {
- public static void main(String[] args) {
- // 匿名内部类的方式创建多线程实现类
- new Thread(new Runnable() {
- @Override
- public void run() {
- System.out.println("使用匿名内部类实现, 当前线程的名称:" + Thread.currentThread().getName());
- }
- }).start();// 启动线程
- // 使用 Lambda 表达式创建多线程
- new Thread(() -> {
- System.out.println("使用 Lambda 实现多线程, 当前线程的名称:" + Thread.currentThread().getName());
- }).start();
- }
- }
总结:
匿名内部类的好处和弊端
一方面, 匿名内部类可以帮我们省去实现类的定义; 另一方面, 匿名内部类的语法 -- 确实太复杂了!
语义分析
仔细分析该代码中的语义, Runnable 接口只有一个 run 方法的定义:
public abstract void run();
即制定了一种做事情的方案(其实就是一个函数):
无参数: 不需要任何条件即可执行该方案.
无返回值: 该方案不产生任何结果.
代码块(方法体): 该方案的具体执行步骤.
同样的语义体现在 Lambda 语法中, 要更加简单:
() -> System.out.println("多线程任务执行!")
前面的一对小括号即 run 方法的参数(无), 代表不需要任何条件;
中间的一个箭头代表将前面的参数传递给后面的代码;
后面的输出语句即业务逻辑代码.
Lambda 标准格式
Lambda 省去面向对象的条条框框, 格式由 3 个部分组成:
一些参数
一个箭头
一段代码
Lambda 表达式的标准格式为:
(参数类型 参数名称) -> {
代码语句
}
格式说明:
小括号内的语法与传统方法参数列表一致: 无参数则留空; 多个参数则用逗号分隔.
-> 是新引入的语法格式, 代表指向动作.
大括号内的语法与传统方法体要求基本一致.
Lambda 的使用前提
Lambda 的语法非常简洁, 完全没有面向对象复杂的束缚. 但是使用时有几个问题需要特别注意:
使用 Lambda 必须具有接口, 且要求接口中有且仅有一个抽象方法. 无论是 JDK 内置的 Runnable , Comparator 接口还是自定义的接口, 只有当接口中的抽象方法存在且唯一 时, 才可以使用 Lambda.
使用 Lambda 必须具有上下文推断. 也就是方法的参数或局部变量类型必须为 Lambda 对应的接口类型, 才能使用 Lambda 作为该接口的实例.
有且仅有一个抽象方法的接口, 称为 "函数式接口".
Lambda 省略格式
可推导即可省略
Lambda 强调的是 "做什么" 而不是 "怎么做", 所以凡是可以根据上下文推导得知的信息, 都可以省略.
省略规则
在 Lambda 标准格式的基础上, 使用省略写法的规则为:
小括号内参数的类型可以省略;
如果小括号内有且仅有一个参, 则小括号可以省略;
如果大括号内有且仅有一个语句, 则无论是否有返回值, 都可以省略大括号, return 关键字及语句分号. 要省略必须一起省略
Lambda 表达式的练习
题目
给定一个厨子 Cook 接口, 内含唯一的抽象方法 makeFood , 且无参数, 无返回值.
接口代码
- package demo04;
- public interface Cook {
- public abstract void makeFood();
- }
解答
- package demo04;
- public class CookTest {
- public static void main(String[] args) {
- // 接口作为参数, 传递的是实现类对象
- invokeFood(new Cook() {
- @Override
- public void makeFood() {
- System.out.println("匿名内部类实现吃饭了");
- }
- });
- invokeFood(() -> {
- System.out.println("Lambda 表达式实现吃饭了");
- });
- }
- public static void invokeFood(Cook cook) {
- // 调用实现类对象里的方法
- cook.makeFood();
- }
- }
执行后结果
题目
使用数组存储多个 Person 对象
对数组中的 Person 对象使用 Arrays 的 sort 方法通过年龄进行升序排序
定义 Person 类
- package demo05;
- public class Person {
- private String name;
- private int age;
- public Person(String name, int age) {
- this.name = name;
- this.age = age;
- }
- public Person() {
- }
- @Override
- public String toString() {
- return "Person{" +
- "name='" + name + '\'' +
- ", age=" + age +
- '}';
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public int getAge() {
- return age;
- }
- public void setAge(int age) {
- this.age = age;
- }
- }
解答
- package demo05;
- import java.util.Arrays;
- /*
- Lambda 表达式有参数有返回值的练习
- 需求:
- 使用数组存储多个 Person 对象
- 对数组中的 Person 对象使用 Arrays 的 sort 方法通过年龄进行升序排序
- */
- public class Demo01Arrays {
- public static void main(String[] args) {
- // 使用数组存储多个 Person 对象
- Person[] arr = {
- new Person("柳岩", 38),
- new Person("迪丽热巴", 18),
- new Person("古力娜扎", 19)
- };
- // 对数组中的 Person 对象使用 Arrays 的 sort 方法通过年龄进行升序 (前边 - 后边) 排序
- /*Arrays.sort(arr, new Comparator<Person>() {
- @Override
- public int compare(Person o1, Person o2) {
- return o1.getAge()-o2.getAge();
- }
- });*/
- // 使用 Lambda 表达式, 简化匿名内部类
- Arrays.sort(arr, (Person o1, Person o2) -> {
- return o1.getAge() - o2.getAge();
- });
- // 优化省略 Lambda
- Arrays.sort(arr, (o1, o2) -> o1.getAge() - o2.getAge());
- // 遍历数组
- for (Person p : arr) {
- System.out.println(p);
- }
- }
- }
代码执行的结果
来源: http://www.bubuko.com/infodetail-3336604.html