目录
什么是Lambda表达式?
Lambda表达式是一个匿名函数,用于对一个接口进行非常简洁的实现。
现有的接口实现方式:将一个减法运算抽象为一个接口
public interface Reduce {
int reduce(int a, int b);
}
- 接口实现类
- 匿名内部类
- Lambda表达式
public class Test {
public static void main(String[] args) {
// 接口实现类
Reduce r = new MyReduce();
r.reduce(2, 1);
// 匿名内部类
Reduce r_1 = new Reduce() {
@Override
public int reduce(int a, int b) {
return a - b;
}
};
r_1.reduce(2, 1);
// Lambda表达式
Reduce r_2 = (a, b) -> a - b;
r_2.reduce(2,1);
}
}
class MyReduce implements Reduce {
@Override
public int reduce(int a, int b) {
return a - b;
}
}
interface Reduce {
int reduce(int a, int b);
}
可以看出Lambda表达式在三种接口实现方式中是最简洁的
Lambda表达式对接口的要求
Lambda表达式虽然可以对某些接口进行简单的实现,但并不是所有接口都能使用Lamda表达式进行实现。
要求接口只能有一个抽象方法(default除外)
@FunctionalInterface
修饰函数式接口的注解,接口中抽象方法只有一个(default除外)的接口成为函数式接口
Lambda表达式基础语法
Lambda表达式是一个匿名函数,既然是函数,那么就有以下几个组成部分.
返回值(Lambda无需显式指定),方法名(匿名函数无需),参数列表,方法体 → 剩下:参数列表,方法体
所以对于一个Lambda表达式最主要的就是参数列表和方法体,语法如下:
() : 用来描述参数列表
{} : 用来描述方法体
-> : Lambda运算符,读作goes to
根据参数个数与有无返回,建立六个不同类型接口,分别对其使用Lambda表达式进行实现
public class Test {
public static void main(String[] args) {
// 无参无返回接口
NoParamNoReturn npnr = () -> {
// 要实现的逻辑
System.out.println("Hello World");
};
npnr.test();
// 单参无返回接口
SingleParamNoReturn spnr = (int a) -> {
System.out.println(a);
};
spnr.test(1);
// 多参无返回接口
MultipleParamNoReturn mpnr = (int a, int b) -> {
System.out.println(a - b);
};
mpnr.test(2, 1);
// 无参有返回接口
NoParamSingleReturn npsr = () -> {
return 1;
};
npsr.test();
// 单参有返回接口
SingleParamSingleReturn snsr = (int a) -> {
return a;
};
snsr.test(2);
// 多参有返回接口
MultipleParamSingleReturn mpsr = (int a, int b) -> {
return a - b;
};
mpsr.test(2,1);
}
}
@FunctionalInterface // 无参无返回接口
interface NoParamNoReturn {
void test();
}
@FunctionalInterface // 单参无返回接口
interface SingleParamNoReturn {
void test(int a);
}
@FunctionalInterface // 多参无返回接口
interface MultipleParamNoReturn {
void test(int a, int b);
}
@FunctionalInterface // 无参有返回接口
interface NoParamSingleReturn {
int test();
}
@FunctionalInterface // 单参有返回接口
interface SingleParamSingleReturn {
int test(int a);
}
@FunctionalInterface // 多参有返回接口
interface MultipleParamSingleReturn {
int test(int a, int b);
}
Lambda表达式语法精简
1、由于接口的抽象方法已经定义了参数的数量和类型,所以能够省略参数类型。(如果需要省略类型,要么一起省略,要么都不省略)
(int a, int b) -> { xxx };
(a, b) -> { xxx }; // 一起省略
(int a, b) -> { xxx }; //错误写法
2、参数列表中参数只有一个,则小括号可以省略
(int a) -> { xxx };
(a) -> { xxx }; // 省略类型
a -> { xxx }; // 单个参数省略小括号
3、实现的方法体中只有一条语句,可省略大括号
(int a) -> { System.out.println("hello world"); };
a -> System.out.println("hello world"); // 单条语句省略大括号
4、实现的方法体中只有一条语句,且这条语句是返回语句(return),则大括号和return关键字同时省略
() -> { return 10; };
() -> 10; // 省略大括号和return
(int a, int b) -> { return a + b; };
(a, b) -> a + b
Lambda表达式进阶
方法引用
在某些需求下,我们可能会重复的去执行某一个逻辑,可能出现下面这种情况
// 不同的地方都在使用这个逻辑,一旦需求变更,就要到整个项目去修改a -> a + 1的逻辑
SingleParamSingleReturn lambda = a -> a + 1;
SingleParamSingleReturn lambda_2 = a -> a + 1;
所以我们可以将此逻辑归纳到一个方法里面,这样即使逻辑变更,也只需要修改add方法
public class Test {
public static void main(String[] args) {
SingleParamSingleReturn lambda = a -> add(a);
SingleParamSingleReturn lambda_2 = a -> add(a);
}
public static int add(int a) {
return a + 1;
}
}
@FunctionalInterface // 单参有返回接口
interface SingleParamSingleReturn {
int test(int a);
}
但是这样写依旧不够简洁,所以我们可以使用方法引用
方法引用可以快速的将一个Lambda表达式指向一个已经实现的方法
语法:方法隶属者::方法名。方法隶属者:静态方法隶属者是其所在类,普通方法是其所在类的对象
public class Test {
public static void main(String[] args) {
SingleParamSingleReturn lambda = Test::add;
SingleParamSingleReturn lambda_2 = Test::add;
}
public static int add(int a) {
return a + 1;
}
}
@FunctionalInterface // 单参有返回接口
interface SingleParamSingleReturn {
int test(int a);
}
要求:方法引用中的方法,必须与引用它的接口中的抽象方法参数列表,返回类型一致(对比上方代码抽象方法和引用方法的参数及返回类型)
构造方法的引用
语法:构造方法隶属者::new
public class Test {
public static void main(String[] args) {
// 普通方式
PersonCreater pc = () -> new Person();
// 构造方法引用,至于new到底调用的是有参还是无参构造函数是根据接口中的抽象方法来决定的
// 抽象方法的参数列表和返回类型与哪一个构造方法一致,就调用的是哪一个构造方法
PersonCreater pc_1 = Person::new;
pc_1.getPerson();
PersonCreater2 pc_2 = Person::new;
pc_2.getPerson("小明", 18);
}
}
class Person {
public String name;
public int age;
Person() {
System.out.println("无参构造执行");
}
Person(String name, int age) {
this.name = name;
this.age = age;
System.out.println("有参构造执行");
}
}
@FunctionalInterface
interface PersonCreater {
Person getPerson();
}
@FunctionalInterface
interface PersonCreater2 {
Person getPerson(String name, int age);
}
系统内置的函数式接口
有很多,只介绍四个基本的
Predicate<T>:参数T,返回值boolean。主要用于对传入参数进行判断
Predicate<Integer> p = (a) -> a > 2;
Consumer<T>:参数T,返回值void。消费者,主要用于对传入对象的操作
Consumer<String> c = (item) -> System.out.println(item);
Function<T, R>:参数T,返回值R。
Function<String, Boolean> f = (a) -> a.equals("123");
Supplier<T>: 无参数,返回值T。提供者,主要用于提供对象
Supplier<Integer> s = () -> 2;
由Lambda表达式看forEach原理
看一段代码
// 给数组中的每一个元素加1
List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3));
list.forEach(item -> item = item + 1);
要知道forEach原理,首先看为什么我们要传一个Lambda表达式给forEach方法?看源码
default void forEach(Consumer<? super T> action) { xxx }
我们能够发现forEach需要函数式接口Consumer,所以换一个更容易理解的写法,先对Consumer进行实现,再传递给forEach
list.forEach(item -> item = item + 1);
// 等价
Consumer<Integer> c = item -> item = item + 1;
list.forEach(c);
然后分析,Consumer接口实现是实现了,谁在传值,谁在调用?
所以forEach的代码可以等价于,这样就是我们容易理解的方式
List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3));
list.forEach(item -> item = item + 1);
// 等价于
List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3));
Consumer<Integer> c = item -> item = item + 1;
for (Interger t : list) {
c.accept(t);
}