摘要:Lambda表达式总结
前言:今天总结一下Lambda表达式。
Lambda表达式基本介绍
可以把Lambda表达式理解为简洁地表示可传递的匿名函数的一种方式:它没有名称,但它有参数列表、函数主体、返回类型,还可能有可以抛出的异常列表。
- 匿名————它不像普通的方法那样有一个明确的名称:写得少而想得多!
- 函数————Lambda表达式不像方法那样属于某个特定的类,但和方法一样,Lambda有参数列表、函数主体、返回类型,还可能有可以抛出的异常列表。
- 传递————Lambda表达式可以作为参数传递给方法或存储在变量中。
- 简洁————无需像匿名类那样写很多模板代码。
Lambda表达式鼓励我们采用行为参数化风格,例如下面的代码:
1 | 没有使用Lambda表达式: |
有效的Lambda表达式:
(String s) -> s.length();
(Apple a) -> g.getWeight() > 150;
(int x, int y) -> {
System.out.println(“Result:”);
System.out.println(x+y);
};
() -> 42;
(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
Lambda表达式基本语法是:
(parameters) -> expression
或
(parameters) -> {statements;}
错误语法:
1 | (Integer i) -> return "Alan" + 1; |
函数式接口
函数式接口:只定义一个抽象方法的接口。
利用函数式接口来传递Lambda:
- 行为参数化
- 使用函数式接口来传递行为
- 执行一个行为
- 传递lambda
Function常用函数接口:
| name | type | description |
|---|---|---|
| Consumer | Consumer< T > | 接收T对象,不返回值 |
| Predicate | Predicate< T > | 接收T对象并返回boolean |
| Function | Function< T, R > | 接收T对象,返回R对象 |
| Supplier | Supplier< T > | 提供T对象(例如工厂),不接收值 |
| UnaryOperator | UnaryOperator | 接收T对象,返回T对象 |
| BinaryOperator | BinaryOperator | 接收两个T对象,返回T对象 |
基本示例:
| 使用案例 | lambda示例 | 对应的函数式接口 |
|---|---|---|
| 布尔表达式 | (List |
Predicate<List |
| 创建对象 | () -> new Apple(10) | Supplier |
| 消费一个对象 | (Apple a) -> { System.out.println(a.getWeight);} | Consumer |
| 从一个对象中选取/抽取 | (String s) -> s.length() | Function<String, Integer> 或 <ToIntFunction |
| 组合两个值 | (int a, int b) -> a * b | IntBinaryOperator |
| 比较两个对象 | (Apple a1, Apple a2) -> a1.getWeight.compareTo(a2.getWeight()) | BiFunction<Apple, Apple, Integer> 或 ToIntBiFunction<Apple, Apple> |
方法引用
第一个方法引用:
1 |
|
方法引用可以被看作仅仅调用特定方法的Lambda的一种快捷写法。它的基本思想是,如果一个Lambda代表的只是“直接调用这个方法”,那最好还是用名称来调用它,而不是去描述如何调用它。方法引用就是让你根据已有的方法实现来创建Lambda表达式。
1 | 当你需要使用方法引用时:目标引用放在分隔符::前,方法的名称放在后面 |
方法引用主要有三类:
- 指向静态方法的方法引用
- 指向任意类型实例方法的方法引用
- 指向现有对象的实例方法的引用
1 | 具体实例如下所示: |
构造函数引用
无参构造函数引用:
Supplier
Apple a1 = c1.get();
等价于:
Supplier
Apple a1 = c1.get();
一个构造函数引用:
Function<Integer, Apple> c2 = Apple::new;
Apple a2 = c2.apply(110);
等价于:
Function<Integer, Apple> c2 = (weight) -> new Apple(weight);
Apple a2 = c2.apply(110);
两个构造函数引用:
BiFunction<String, Integer, Apple> c3 = Apple::new;
Apple c3 = c3.apply(“green”, 110)
等价于:
BiFunction<String, Integer, Apple> c3 = (color, weight) -> new Apple(color, weight);
Apple c3 = c3.apply(“green”, 110)
三个构造函数引用:
我们需要有一个与构造函数引用的签名匹配的函数式接口,但是语言本身没有给我们提供一个这样的函数式的接口,所以我们需要自己创建一个:
1 | public interface TriFunction<T, U, V, R>{ |
复合Lambda表达式
- 排序:Comparator
c = Comparator.comparing(Apple::getWeight); - 逆序:inventory.sort(comparing(Apple::getWeight).reversed());
- 比较链:inventory.sort(comparing(Apple::getWeight).reversed().thenComparing(Apple::getWeight));
- 谓词复合:negate、and、or
- negate:Predicate
notRedApple = redApple.negate();//产生现有Predicate对象redApple的非 - and、or:Predicate
redAndHeavyAppleGreen = redApple.and(a -> a.getWeight() > 150).or(a -> “green”.equals(a.getColor()));//要么是重150以上的红苹果,要么是绿苹果。
- negate:Predicate
- 函数复合:Function接口为此配了andThen和compose两个默认方法,它们都会返回Function的一个实例。
1
2
3
4
5
6
7
8
9
10
11
12Function<Integer, Integer> f = x -> x + 1;
Function<Integer, Integer> g = x -> x * 2;
Function<Integer, Integer> h = f.andThen(g);
int result = h.apply(1);//result为4
Function<Integer, Integer> f = x -> x + 1;
Function<Integer, Integer> g = x -> x * 2;
Function<Integer, Integer> h = f.compare(g);
int result = h.apply(1);//result为3
compare:f(g(x))
andThen:g(f(x))
lambda表达式示例
1.用lambda表达式实现Runnable
1 | // Java 8之前: |
2.使用Java 8 lambda表达式进行事件处理
1 | // Java 8之前: |
3.使用lambda表达式对列表进行迭代
1 | // Java 8之前: |
4.使用lambda表达式和函数式接口Predicate
1 | public static void main(args[]){ |
5.如何在lambda表达式中加入Predicate
1 | // 甚至可以用and()、or()和xor()逻辑函数来合并Predicate, |
6.Java 8中使用lambda表达式的Map和Reduce示例
1 | // 不使用lambda表达式为每个订单加上12%的税 |
7.通过过滤创建一个String列表
1 | // 创建一个字符串列表,每个字符串长度大于2 |
8.对列表的每个元素应用函数
1 | // 将字符串换成大写并用逗号链接起来 |
9.复制不同的值,创建一个子列表
1 | // 用所有不同的数字创建一个正方形列表 |
10.计算集合元素的最大值、最小值、总和以及平均值
1 | //获取数字的个数、最小值、最大值、总和以及平均值 |
Lambda表达式要点
- lambda表达式仅能放入如下代码:
- 预定义使用了 @Functional 注释的函数式接口,自带一个抽象函数的方法,或者SAM(Single Abstract Method 单个抽象方法)类型。这些称为lambda表达式的目标类型,可以用作返回类型,或lambda目标代码的参数。例如,若一个方法接收Runnable、Comparable或者 Callable 接口,都有单个抽象方法,可以传入lambda表达式。类似的,如果一个方法接受声明于 java.util.function 包内的接口,例如 Predicate、Function、Consumer 或 Supplier,那么可以向其传lambda表达式。
- lambda表达式内可以使用方法引用,仅当该方法不修改lambda表达式提供的参数。本例中的lambda表达式可以换为方法引用,因为这仅是一个参数相同的简单方法调用。
1 |
|
- lambda内部可以使用静态、非静态和局部变量,这称为lambda内的变量捕获。
- Lambda表达式在Java中又称为闭包或匿名函数。
- Lambda方法在编译器内部被翻译成私有方法,并派发 invokedynamic 字节码指令来进行调用。可以使用JDK中的 javap 工具来反编译class文件。使用 javap -p 或 javap -c -v 命令来看一看lambda表达式生成的字节码。大致应该长这样:
1 | private static java.lang.Object lambda$0(java.lang.String); |
- lambda表达式有个限制,那就是只能引用 final 或 final 局部变量,这就是说不能在lambda内部修改定义在域外的变量。
1 | List<Integer> primes = Arrays.asList(new Integer[]{2, 3,5,7}); |
- 本文作者: th3ee9ine
- 本文链接: https://www.blog.ajie39.top/2021/05/05/Lambda表达式/
- 版权声明: 本博客所有文章除特别声明外,均采用 LICENSE 下的许可协议。转载请注明出处!