19.lambda表达式
Lambda表达式
lambda表达式体现的是函数编程思想
需求: 启动一个线程, 在控制台输出一句话: 多线程程序启动了
方式一:
- 定义MyRunnable类实现Runnable接口, 重写run方法
- 创建Thread类, 将MyRunable的对象作为构造参数
- 启动线程
方式二:
- 匿名内部类的方式改进
方式三:
- Lambda表达式的方式改进
MyRunnable
package myLambda;
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("多线程启动了");
}
}
LambdaDemo
package myLambda;
public class LambdaDemo {
public static void main(String[] args) {
// 实现类的方式实现需求
/*
MyRunnable my = new MyRunnable();
Thread t = new Thread(my);
t.start();
*/
// 匿名内部类
/*
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("多线程启动了");
}
}).start();
*/
// Lambda表达式方式改进
new Thread(() -> {
System.out.println("多线程启动了");
}).start();
}
}
Lambda表达式标准格式
匿名内部类重写run方法的代码分析
- 方法形式参数为空, 说明调用方法时不需要传递参数
- 方法返回值类型为void, 说明方法执行没有结果返回
- 方法体重的内容, 使我们具体要做的事情
Lambda表达式的代码分析
-
(): 里面没有内容, 可以看成是方法形式参数为空
-
->: 箭头指向后面要做的事情
-
{}: 包含一段代码, 称之为代码块, 可以看成是方法体中的内容
组成lambda表达式三要素: 形式参数, 箭头, 代码块
lambda表达式格式
- 格式: (形式参数) ->
- 形式参数: 如果有多个参数, 参数之间用逗号隔开; 如果没有参数, 留空即可
- ->: 由英文中画线和大于符号组成, 固定写法,
- 代码块: 是具体要做的事情, 也就是以前我们写的方法体内容
练习
不带参数
Eatable
package myLambda.demo;
public interface Eatable {
void eat();
}
EatableImpl
package myLambda.demo;
public class EatableImpl implements Eatable {
@Override
public void eat() {
System.out.println("一天一苹果");
}
}
EatableDemo
package myLambda.demo;
public class EatableDemo {
public static void main(String[] args) {
// 调用useEatable方法
Eatable e = new EatableImpl();
useEatable(e);
// 匿名内部类
useEatable(new Eatable() {
@Override
public void eat() {
System.out.println("一天一苹果");
}
});
// lambda表达式 不带参数
useEatable(() -> {
System.out.println("一天一苹果");
});
}
private static void useEatable(Eatable e) {
e.eat();
}
}
带参数
Flyable
package myLambda.demo2;
public interface Flyable {
void fly(String s);
}
FlyableDemo
package myLambda.demo2;
public class FlyableDemo {
public static void main(String[] args) {
// 匿名内部类
useFlyable(new Flyable() {
@Override
public void fly(String s) {
System.out.println(s);
System.out.println("飞机自驾游");
}
});
// lambda表达式带参数
useFlyable((String s) -> {
System.out.println(s);
System.out.println("飞机自驾游");
});
}
private static void useFlyable(Flyable f) {
f.fly("风和日丽");
}
}
多个参数
Addable
package myLambda.demo3;
public interface Addable {
int add(int x, int y);
}
AddableDemo
package myLambda.demo3;
public class AddableDemo {
public static void main(String[] args) {
// lambda表达式实现, 多参数
useAddable((int x, int y) -> {
// return x + y;
return x - y; // 这里是方法add的具体实现代码块
});
}
private static void useAddable(Addable a) {
int sum = a.add(10, 20);
System.out.println(sum);
}
}
lambda省略模式
省略规则
- 参数类型可以省略, 多个参数类型不能单独省略一个
- 如果参数仅有一个, 小括号可以省略
- 如果代码块只有一句, 可以省略大括号和分号, 甚至是return
Addable
package myLambda.demo4;
public interface Addable {
int add(int x, int y);
}
Flyable
package myLambda.demo4;
public interface Flyable {
void fly(String s);
}
LambdaDemo
package myLambda.demo4;
public class LambdaDemo {
public static void main(String[] args) {
/*
useAddable((int x, int y) -> {
return x + y;
});
*/
// 省略模式一: 参数类型可以省略
useAddable((x, y) -> {
return x + y;
});
// 有多个参数时, 不能单独省略一个, 要么都省略,要么都不省略
/*
useFlyable((String s) -> {
System.out.println(s);
});
*/
useFlyable((s) -> {
System.out.println(s);
});
// 省略模式二: 如果参数只有一个, 可以省略小括号
useFlyable(s -> {
System.out.println(s);
});
// 省略模式三: 如果代码块语句只有一条, 可以省略大括号和分号
useFlyable(s -> System.out.println(s));
}
private static void useFlyable(Flyable f) {
f.fly("晴空万里");
}
private static void useAddable(Addable a) {
int sum = a.add(10, 20);
System.out.println(sum);
}
}
lambda表达式注意事项
注意:
- 使用Lambda必须要有接口, 并且接口中有且仅有一个抽象方法
- 必须有上下文环境, 才能推导出Lambda对应的接口
- 根据局部变量赋值得知lambda对应接口: Runnable r = () -> System.out.println("lambda表达式");
- 根据调用方法的参数得知lambda对应接口: new Thread(() -> System.out.println("lambda表达式")).start();
Inter
package myLambda.demo5;
public interface Inter {
void show();
// void method();
}
LambdaDemo
package myLambda.demo5;
public class LambdaDemo {
public static void main(String[] args) {
/*
useInter(() -> {
System.out.println("好好学习, 天天向上");
});
*/
// lambda表达式前提: 必须有接口, 并且接口中有且只有一个抽象方法
useInter(() -> System.out.println("好好学习, 天天向上"));
// 使用lambda, 必须有上下文环境, 才能推导出lambda对应的接口
/*
new Thread((new Runnable() {
@Override
public void run() {
System.out.println("匿名内部类");
}
})).start();
Runnable r = () -> System.out.println("lambda表达式");
new Thread(r).start();
*/
new Thread(() -> System.out.println("lambda表达式")).start();
}
private static void useInter(Inter i) {
i.show();
}
}
lambda表达式和匿名内部类区别
所需类型不同:
- 匿名内部类: 可以是接口, 也可以是抽象类, 还可以是具体类
- lambda表达式: 只能是接口
使用限制不同:
- 如果接口中仅有一个抽象方法, 可以使用lambda表达式, 也可以使用匿名内部类
- 如果接口中多余一个抽象方法, 只能使用匿名内部类, 而不能使用lambda表达式
实现原理不同:
- 匿名内部类: 编译之后, 会产生单独的.class字节码文件
- lambda表达式: 编译之后, 没有一个单独的.class字节码文件. 字节码文件会在运行时动态生成
Animal
package myLambda.demo6;
public abstract class Animal {
public abstract void method();
}
Inter
package myLambda.demo6;
public interface Inter {
void show();
// void show2();
}
Student
package myLambda.demo6;
public class Student {
public void study() {
System.out.println("热爱生活!");
}
}
Demo
package myLambda.demo6;
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("具体类");
}
});
// lambda方式
// useInter(() -> System.out.println("接口"));
// useAnimal(() -> System.out.println("抽象类")); // lambda必须是一个接口
// useStudent(() -> System.out.println("具体类")); // lambda必须是一个接口
// 接口中有多个抽象方法
/*
useInter(new Inter() {
@Override
public void show() {
System.out.println("show");
}
@Override
public void show2() {
System.out.println("show2");
}
});
*/
}
private static void useStudent(Student s) {
s.study();
}
private static void useAnimal(Animal a) {
a.method();
}
private static void useInter(Inter i) {
i.show();
}
}
衣带渐宽终不悔,为伊消得人憔悴!