Lambda表达式
Lambda表达式
函数式编写思想概述

在数学中,函数就是输入量、输出量的一套计算方案,也就是“拿什么东西做什么事情”。相对而言,面向对象过分强调“必须通过对象的形式来做事情”,而函数式思维则是尽量忽略面向对象的复杂语法--强调做什么么,而不是怎么做。
面向对象的思维:
做一件事情,找一个能解决这个事情的对象,调用对象的方法,完成任务。
函数式编程思想:
只要能获得结果,谁去做,怎么做的都不在乎,重视的是结果而不是过程。
冗余的Runnable代码
1、传统写法
package day07.day07_2; //Runnbale实现类 public class RunnableImpl implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName()+" 新线程被创建了"); } }
package day07.day07_2; //了解Runnable的代码冗余现象 public class demo { public static void main(String[] args) { //第一种方式 //创建实现类对象 RunnableImpl run = new RunnableImpl(); //创建Thread 传递run Thread t=new Thread(run); //开启线程 t.start();//Thread-0 新线程被创建了 //方式二--简化 创建匿名内部类(无需单独创建实现类) Runnable r = new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+" 新线程被创建了"); } }; new Thread(r).start(); //最简形式 new Thread( new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+" 新线程被创建了"); } } ).start(); } }
2、代码分析
对于Runnable的匿名内部类用法,可以分析出:
Thread类需要Runnable接口作为参数,其中的run方法是用来指定线程任务内容的核心。
1.为了指定run的方法体,不得不需要Runnable接口的实现类。
2.为了省去RunnableImpl实现类的麻烦,不得不使用匿名内部类。
3.必须覆盖重写run方法,所以方法名、方法参数、方法返回值不得不重写,且不能写错。
4.而实际上,似乎只有方法体才是关键所在
编程思想转换
1、做什么,而不是怎么做
我们真的希望创建一个匿名内部类对象吗?我们只是为了做这件事不得不创建一个对象。我们真正希望做的事情是:将run方法体内的代码传递给Thread知晓。
传递一段代码--->这才是我们真正的目的。
2、生活举例

当我们需要从广州到北京时,可以选择高铁、汽车、徒步或者骑行。我们的真正目的是到达北京,而如何才能到达北京的形式并不重要,所以我们一直探索有没有比高铁更好的方式---搭乘飞机

而这种飞机已经诞生:2014年Oracle发布的Java8(JDK1.8)中,加入了lambda表达式的重量级新特性。
体验Lambda的更优写法
借助Java8的全新语法,上述Runnable接口的匿名内部类写法可以通过最简单Lambda表达式达到同样效果
例如:
public class damo_Lambda { 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(Thread.currentThread().getName()+"新的线程被创建了"); } ).start(); /** * Thread-0 新线程被创建了 * Thread-1新的线程被创建了 */ } }
不再有"不得不创建接口对象"的束缚,不再有“抽象方法覆盖重写”的负担
匿名内部类的好处与弊端
一方面,匿名内部类可以帮我们省去实现类;另一方面,匿名内部类的语法---过于复杂
1、内部类语义分析
仔细分析该代码的语义,Runnable接口只有一个run方法的定义:
public abstract void run();
即制定了一种做事方案(其实就是一个函数):
无参:不需要任何条件就可以执行
无返回值:该方案不产生结果
代码块(方法体):该方案的具体执行步骤。
2、同样语义,在Lambd语法中
() -> {System.out.println("多线程执行")}
前面的一对小括号(),即run方法的参数(无),表示无需任何条件
中间的箭头->,代表将前面的参数传递给后面的代码
后面的{}中的语句,即业务逻辑代码
Lambda表达式格式
1、Lambda标准表达式:
由三部分组成:
a.一些参数
b.一个箭头
c.一段代码
2、格式:
(参数列表) -> {一些重写方法}
3、解析:
():接口中抽象方法的参数列表,没有参数就空着;有参数就写出参数,多个参数逗号隔开
->:传递的意思,把方法传递给方法体
{}:重写接口的抽象方法的方法体
练习:使用Lambda标准格式(无参)
需求:给定一个厨子 Cook 接口,内含唯一的抽象方法 makeFood ,且无参数、无返回值 。
package day07.day07_3; //创建Cook接口 public interface Cook { //定义makeFood抽象方法 public abstract void makeFood(); }
package day07.day07_3; public class demo { public static void main(String[] args) { method( new Cook() { //重写接口中的抽象方法 @Override public void makeFood() { System.out.println("吃饭啦!!!"); } } ); //使用Lambda表达式 method( ()->{ System.out.println("Lambda吃饭啦"); } ); } //定义静态方法 传递Cook对象 public static void method(Cook cook){ //调用Cook中的方法 cook.makeFood(); } }
Lambda的参数和返回值
需求:
使用数组存储多个Person对象
对数组中的Person对象使用Arrays.sort方法,通过年龄进行升序排序
package day07.day07_4; //创建Person类 public class Person { //成员变量 private String name; private int age; //构造方法 public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } //get & set 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; } //重写toString @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
package day07.day07_4; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; public class dome { public static void main(String[] args) { //创建数组 存储Person对象 Person[] arr = { new Person("花千骨",18), new Person("白子画",40), new Person("杀阡陌",20) }; //1.使用Arrays.sort方法 按照年龄升序 // Arrays.sort(arr, new Comparator<Person>() { // @Override // public int compare(Person o1, Person o2) { // return o1.getAge()-o2.getAge(); // } // }); //2.使用Lambda表达式 Arrays.sort(arr,(Person o1, Person o2)->{ return o1.getAge()-o2.getAge(); }); //利用for循环打印 for (Person p:arr ) { System.out.println(p.getName()+"--->"+p.getAge()); } /** * 花千骨--->18 * 杀阡陌--->20 * 白子画--->40 */ } }
练习:使用Lambda标准格式(含参与返回值)
需求:
给定一个计算器Calculator接口,内含抽象方法calc可以将两个int数字相加得到和值
package day07.day07_5; //定义计算器接口 public interface Calculator { //定义求和抽象方法 public abstract int calc(int a,int b); }
package day07.day07_5; public class demo { public static void main(String[] args) { //内部类写法 invokeCalc(10, 20, new Calculator() { @Override public int calc(int a, int b) { return a+b; } }); //Lambda写法 invokeCalc(5,8,(int a,int b)->{ return a+b; }); } private static void invokeCalc(int a ,int b,Calculator cal){ int result = cal.calc(a,b); System.out.println("计算结果为:"+result); } }
Lambda省略格式
Lambda强调(做什么而不是怎么做),所有凡是可以根据上下文推导得知的信息,都可以省略
省略规则
1.(参数列表):括号中参数列表的数据类型,可以省略不写
package day07.day07_5; //定义计算器接口 public interface Calculator { //定义求和抽象方法 public abstract int calc(int a,int b); }
package day07.day07_5; public class demo { public static void main(String[] args) { //内部类写法 invokeCalc(10, 20, new Calculator() { @Override public int calc(int a, int b) { return a+b; } }); //Lambda写法 invokeCalc(5,8,(int a,int b)->{ return a+b; }); //Lambda省略写法 invokeCalc(5,8,(a,b)->{ return a+b; }); } private static void invokeCalc(int a ,int b,Calculator cal){ int result = cal.calc(a,b); System.out.println("计算结果为:"+result); } }
2.(参数列表):括号中参数只有一个,类型和括号都可以不写
3.{一些代码}:如果{}中的代码只有一行,无论是否有返回值都可以省略{},return,;
package day07.day07_5; //定义计算器接口 public interface Calculator { //定义求和抽象方法 public abstract int calc(int a,int b); }
package day07.day07_5; public class demo { public static void main(String[] args) { //内部类写法 invokeCalc(10, 20, new Calculator() { @Override public int calc(int a, int b) { return a+b; } }); //Lambda写法 invokeCalc(5,8,(int a,int b)->{ return a+b; }); //Lambda省略写法 invokeCalc(5,8,(int a,int b)->a+b); } private static void invokeCalc(int a ,int b,Calculator cal){ int result = cal.calc(a,b); System.out.println("计算结果为:"+result); } }
练习:Lambda省略格式
需求:
使用省略写法完成Cook练习
package day07.day07_6; public interface Cook { public abstract void makeFood(); }
package day07.day07_6; public class demo { public static void main(String[] args) { //使用Lambda省略格式 method(()-> System.out.println("Lambda吃的真少呢")); } //创建静态方法 传递Cook对象 public static void method(Cook cook){ //调用cook的makefood方法 cook.makeFood(); } }
Lambda的使用前提
Lambda的语法非常简洁,完全没有面向对象的复杂束缚,但是需要注意:
1、使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法
无论是Runnable、还是Comparator接口、还是自定义接口,只有当接口中的抽象方法存在,才能使用Lambda
2、使用Lambda必须具有上下文推断
也就是方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为接口
3、有且仅有一个抽象方法的接口,称为“函数式接口”

浙公网安备 33010602011771号