文章目录
Lambda表达式
函数式编程思想
- 数学中,函数就是有输入量、输出量的一套计算方案,= 拿数据做操作;
- 面向对象思想:必须通过对象的形式来做事情;
- 函数式思想:强调做什么,而不是以什么形式去做;
- Lambda表达式就是函数式思想的体现,也就是强调的是什么而不是以什么形式去做。
案例:
需求:启动一个线程,在控制台输出一句话:多线程程序启动了。
实现方法:
方法1:使用Runnable实现类创建对象
1 创建MyRunnable类实现Runnable接口,重写run方法
public class MyRunnable implements Runnable(){
@Override
public void run(){
System.out.println("多线程启动了!");
}
}
2 创建MyRunnable类对象
MyRunnable mr = new MyRunnable();
3 创建Thread对象,参数是MyRunnable类对象
Thread t = new Thread(mr);
4 启动线程
t.start();
方法2:匿名内部类的方式改进(接口的实现类、父类的子类)
new Thread(new Runnable{
@Override
public void run(){
....
}
}).start();
方法3:Lambda表达式的方式改进
new Thread(() -> {
.....
}).start();
表示的就是:Lambda表达式就是 实现了Runnable接口的实现类。
由于Thread构造方法的参数是Runnable接口,且满足只有一个抽象方法run,所以可以使用Lambda表达式
代码:
// itiheima312.test4
public class Demo1 {
public static void main(String[] args) {
* 通过创建Runnable实现类的方式
// 创建MyRunnable对象(该类是Runnable的实现类)
/*MyRunnable mr = new MyRunnable();
// 创建Thread对象,将MyRunnable对象当参数
Thread t = new Thread(mr);
// 启动线程
t.start();*/
* 匿名内部类的形式
/*new Thread(new Runnable() {
@Override
public void run() {
System.out.println("多线程程序启动了");
}
}).start();*/
* Lambda表达式的方式改进
new Thread( () -> {
System.out.println("多线程程序启动了");
} ).start();
// Thread(Runnable target)构造方法,说明传递是Runnable接口的实现类对象,
// 由于参数是接口,且满足只有一个抽象方法run,所以可以使用run
}
}
// 实现Runnable接口,重写run方法
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("多线程程序启动了");
}
}
Lambda表达式的标准格式
匿名内部类格式说明:
new Thread(new Runnable){
@Override
public void run(){
// 方法形式参数为空,说明调用该方法不需要实参
// 方法返回值是void,说明方法没有返回值
System.out.println("xxxxxxx");
// 方法的主体,是我们具体要实现的内容
}
}).start();
Lambda表达式的标准格式说明:
new Thread(() -> {
// 理解:() = 方法形式参数;-> = 箭头指向后面要做的事情;{} = 代码块
System.out.println("xxxx");
}).start();
Lambda表达式的三要素: 形式参数,箭头,代码块
格式:
(形式参数) -> {代码块}
// 形式参数如果有多个,使用逗号隔开即可;没有则空
// -> 表示指向动作
// 代码块:要执行的具体操作
Lambda表达式的使用前提:
有一个接口;接口中有且仅有一个抽象方法
Lambda表达式的三种情况:
- 抽象方法无参数
方法名/类名( () -> {})
- 抽象方法有参数
方法名/类名((参数类型 参数名) -> {})
- 抽象方法有参数有返回值
方法名/类名((参数类型 参数名) -> {return xxx;})
练习1:抽象方法无参数
需求:
定义接口Eatable,里面定义一个抽象方法,void eat()
定义测试类EatableDemo,在测试类中提供两个方法,useEatable(Eatable e)、main()并在其中调用useEatable方法。
// itiheima312.test5
public class EatableDemo {
public static void main(String[] args) {
* 使用实现接口的类的形式
/* EatableImpl e = new EatableImpl();
useEatable(e);*/
* 使用匿名内部类的形式
/* useEatable(new Eatable() {
@Override
public void eat() {
System.out.println("定义人生");
}
});*/
* Lambda表达式
useEatable(() -> {
System.out.println("sunshine");
});
// 更具上下文环境,useEatable方法的参数是Eatable接口,只有一个抽象方法eat
}
private static void useEatable(Eatable e){
e.eat();
}
}
// Eatable接口
public interface Eatable {
void eat();
}
// Eatable接口的实现类
public class EatableImpl implements Eatable {
@Override
public void eat() {
System.out.println("要自己定义自己,别活在比人的评价体系里。");
}
}
练习2:抽象方法有参数
需求:
定义接口(Flyable),抽象方法:void fly(String s);
定义测试类(FlyableDemo),两个方法:useFlyable(Flyable f)、主方法调用useFlyable
// itiheima312.test6
public class FlyableDemo {
public static void main(String[] args) {
// 通过接口实现类的方式实现
/*FlyableImpl fi = new FlyableImpl();
useFlyable(fi, "xhj");*/
// 匿名内部类的形式
useFlyable(new Flyable() {
@Override
public void fly(String s) {
System.out.println(s + ":梦想靠努力的堆砌");
}
},"xhj");
// Lambda表达式形式
useFlyable((String s) -> {
System.out.println(s + ":理想很丰满,现实很骨感");
},"xhj");
// useFlyable方法的参数是Flyable接口,有且只有一个方法fly
}
private static void useFlyable(Flyable f,String s){
f.fly(s);
}
}
// Flyable接口的实现类FlyableImpl
public class FlyableImpl implements Flyable {
@Override
public void fly(String s) {
System.out.println(s + "自述: 行动要追的上梦想");
}
}
// Flyable接口
public interface Flyable {
void fly(String s);
}
练习3:抽象方法带参数带返回值
需求:
定义接口Addable,抽象方法int add(int x,int y)
测试类AddableDemo,方法:useAddable(Addable a);主方法,调用useAddable方法
代码:
package itiheima312.test7;
public class AddableDemo {
public static void main(String[] args) {
// 使用接口实现类
/*AddableImpl ai = new AddableImpl();
useAddable(ai);*/
// 匿名内部类实现
/* useAddable(new Addable(){
@Override
public int add(int x, int y) {
return x + y;
}
});*/
// Lambda表达式形式
useAddable((int x,int y) -> {
return x+y;
});
// useAddable方法的参数是接口
}
private static void useAddable(Addable a){
int sum = a.add(10, 20);
System.out.println("result:" + sum);
// 这个方法只是说明useAddable方法 执行a.add方法,并将结果输出,
// 但是并没有指定add方法的具体执行操作,而Lambda表达式是实现add方法具体实现的。
// Lambda表达式 x+y 则最后输出是result:30; Lambda表达式 x-y 则最后输出的是result:-10
// 总结 Lambda表达式 实际上就是接口中抽象方法的具体实现
}
}
// Addable接口
public interface Addable {
int add(int x,int y);
}
// Addable接口的实现类AddableImpl
public class AddableImpl implements Addable{
@Override
public int add(int x, int y) {
return x + y;
}
}
Lambda表达式的省略模式
省略规则
参数类型可以省略
多个参数情况下,不能只省略一个
参数如果只有一个,小括号可以省略
代码块语句只有一条,大括号和分号可以省略
代码块语句只有一条。且有return,需要将大括号、分号、return都省略
代码:
// package itiheima312.test8;
public class LambdaDemo {
public static void main(String[] args) {
// 不省略版本
/* useAddable((int x,int y) -> {
return x + y;
});*/
1 可以省略参数的类型
/* useAddable((x,y) -> {
return x + y;
});*/
2 多个参数情况下,不能只省略一个参数的数据类型 此情况会报错。
/* useAddable((int x,y) ->{
return x + y;
});*/
// 这种情况会报错 原本是int x,int y 现在是int x,y
// 不省略版本
/* useFlyable((String s) -> {
System.out.println(s);
});*/
// 省略参数类型
/* useFlyable((s) -> {
System.out.println(s);
});*/
3 若参数有且仅有一个,那么小括号可以省略
/* useFlyable(s ->{
System.out.println(s);
});*/
4 如果代码块的语句只有一条,可以省略大括号和分号.
useFlyable(s -> System.out.println(s));
5 如果代码块只有一条语句,如有return 则需要省略大括号、分号、return
useAddable((x,y) -> x + y);
}
private static void useFlyable(Flyable f){
f.fly("春回大地,万物复苏");
}
private static void useAddable(Addable a){
int sum = a.add(26, 55);
System.out.println("小徐和老徐age sum:" + sum);
}
}
// Addable接口
public interface Addable {
int add(int x,int y);
}
// flyable接口
public interface Flyable {
void fly(String s);
}
Lambda表达式的注意事项
- 使用Lambda必须要接口,并且要求接口中有且仅有一个抽象方法
- 必须有上下文环境,才能推导出Lambda对应的接口
根据局部变量的赋值得知Lambda对象的接口:类或者方法的参数是接口 且接口只有一个抽象方法
Runnable r = () -> System.out.println("逆风翻盘");
new Thread(Runnable r ).start();
根据调用方法的参数得知Lambda对象的接口:
new Thread(() -> System.out.println("向阳而生")).start();
代码:
package itiheima312.test9;
public class LambdaDemo {
public static void main(String[] args) {
// Lambda表达式
/* useInter(() -> {
System.out.println("勤能补拙,所向披靡");
});*/
// 使用匿名内部类的形式实现多线程
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("匿名内部类");
}
}).start();
System.out.println("-----------1");
// 匿名内部类的内容可以通过Lambda表达式替代
Runnable r = ()-> System.out.println("向阳而生");
new Thread(r).start();
System.out.println("-----------2");
new Thread(() -> System.out.println("逆风翻盘")).start();
}
// 输出结果
/*
-----------1
匿名内部类
-----------2
向阳而生
逆风翻盘
并不是按照书写的顺序,不清楚其中的原因是什么?
2022/4/21 因为文字的输出都是和线程启动有关系的,线程的启动不是根据书写顺序的,所以不确定那个线程先执行。
*/
private static void useInter(Inter i){
i.show();
}
}
Lambda表达式和匿名内部类区别
区别 | 匿名内部类 | Lambda表达式 |
---|---|---|
参数不同 | 接口、抽象类、具体类 | 只能是 接口 |
参数要求不同 | 接口中多于一个抽象方法, 只能使用匿名内部类 | 接口中有且仅有一个抽象方法, 可以使用Lambda或匿名内部类 |
编译原理不同 | 编译之后,产生单独的.class字节码文件 | 编译之后,没有单独的.class字节码文件, 对应的字节码文件会在运行的时候动态生成 |
代码:
package itiheima312.test10;
public class Demo {
public static void main(String[] args) {
// 匿名内部类
/*useInter(new Inter() {
@Override
public void show() {
System.out.println("接口");
}
}); // 输出接口 采用匿名内部类形式 参数类型可以是接口
useAnimal(new Animal() {
@Override
public void method() {
System.out.println("抽象类");
}
}); // 输出 抽象类 采用匿名内部类形式 参数形式可以是抽象类
useStudent(new Student(){
@Override
public void study() {
System.out.println("具体类");
}
}); //输出 具体类 采用匿名内部类形式 参数形式可以是具体类。
*/
// 说明 匿名内部类 参数形式 可以是 接口、抽象类、具体类。
System.out.println("--------------");
// Lambda表达式
useInter(() -> {
System.out.println("接口");
});
// 输出 接口 说明Lambda表达式可以调用方法的形参为接口
/* useAnimal(() -> {
System.out.println("抽象类");
});*/
// 抽象类不可以
// useStudent(() -> System.out.println("具体类"));
// 具体类不可以
}
private static void useStudent(Student s){
s.study();
}
private static void useAnimal(Animal a){
a.method();
}
private static void useInter(Inter i){
i.show();
}
}
// 抽象类 Animal
public abstract class Animal {
public abstract void method();
}
// 具体类 Student
public class Student {
public void study(){
System.out.println("现在所做的一切,都是为了成为将来更好的自己。");
}
}
// 接口 Inter
public interface Inter {
void show();
}