Java新特性:Lambda表达式
Java新特性:Lambda表达式
Lambda 表达式(Lambda expression),也可称为闭包(Closure),是 Java(SE)8 中一个重要的新特性。Lambda 表达式允许我们通过表达式来代替功能接口。Lambda 表达式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体(body,可以是一个表达式或一个代码块)
Lambda 表达式可以看作是一个匿名函数,基于数学中的 λ 演算得名。
1、Lambda表达式概述
1.1、Lambda表达式的简介
Lambda 表达式(Lambda expression),也可称为闭包(Closure),是 Java(SE)8 中一个重要的新特性。
Lambda 表达式允许我们通过表达式来代替功能接口。Lambda 表达式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体(body,可以是一个表达式或一个代码块)
Lambda 表达式可以看作是一个匿名函数,基于数学中的 λ 演算得名。
1.2、Lambda表达式的语法
Lambda 表达式的基本语法:
(parameters) -> expression 或 (parameters) -> { statements; }
Lambda 表达式的三个组成部分:
- 参数列表(parameters):类似方法中的形参列表,这里的参数是函数式接口里的参数。这里的参数类型可以明确的声明,也可不声明而由 JVM 隐含的推断。另外当只有一个推断类型时可以省略掉圆括号;
- 箭头(->):连接参数列表和 Lambda 主体,可理解为"被用于"的意思;
- Lambda 主体(expression 或 { statements; }):可以是表达式也可以代码块,是函数式接口里方法的实现。代码块可返回一个值或者什么都不反回,这里的代码块块等同于方法的方法体。如果是表达式,也可以返回一个值或者什么都不反回。
Lambda 表达式样例:
// 1. 不需要参数,返回值为 2 () -> 2// 2. 接收一个参数(数字类型),返回其2倍的值 x -> 2*x// 3. 接受2个参数(数字),并返回他们的和 (x, y) -> x+y// 4. 接收2个int型整数,返回他们的乘积 (int x, int y) -> x*y// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void) (String s) -> System.out.print(s)
1.3、Lambda表达式的要求
虽然说,Lambda 表达式可以在⼀定程度上简化接口的实现。但是,并不是所有的接口都可以使用 Lambda 表达式来简洁实现的。
Lambda 表达式毕竟只是⼀个匿名方法。当实现的接口中的方法过多或者多少的时候,Lambda 表达式都是不适用的。
Lambda 表达式,只能实现函数式接口。
2、函数式接口
2.1、什么是函数式接口
如果说,⼀个接口中,要求实现类必须实现的抽象方法,有且只有⼀个!这样的接口,就是函数式接口。
代码如下(示例):
// 有且只有一个实现类必须要实现的抽象方法,所以是函数式接口interface Demo{ public void action();}
或者(示例):
// 有且只有一个实现类必须要实现的抽象方法,所以是函数式接口interface Demo{ public void action(); public default void action2() { System.out.println("JDK1.8新特性,default默认方法可以有具体的实现"); }}
2.2、函数式接口注解(@FunctionalInterface)
在接口之前,我们可以使用 @FunctionalInterface
注解来判断这个接口是否是⼀个函数式接口。,用如果是函数式接口,没有任何问题。如果不是函数式接口,则会报错。功能类似于 @Override
。
代码如下(示例):
@FunctionalInterfaceinterface Demo{ public void action();}
3、Lambda表达式的使用
3.1、Lambda表达式的基本使用
例1(无参数无返回值):
@FunctionalInterfaceinterface NoParameterNoReturn { // 注意:只能有一个抽象方法 void action();}public class TestDemo { public static void main(String[] args) { NoParameterNoReturn noParameterNoReturn = () -> { System.out.println("无参数无返回值"); }; // action方法的主体内容在上述括号内 noParameterNoReturn.action(); }}
例2(一参数无返回值):
@FunctionalInterfaceinterface OneParameterNoReturn { // 注意:只能有一个抽象方法 void action(int a);}public class TestDemo { public static void main(String[] args) { OneParameterNoReturn oneParameterNoReturn = (int a) -> { System.out.println("一参数无返回值:" + "参数1:" + a); }; // action方法的主体内容在上述括号内 oneParameterNoReturn.action(10); }}
例3(多参数无返回值):
@FunctionalInterfaceinterface MoreParameterNoReturn { // 注意:只能有一个抽象方法 void action(int a, int b);}public class TestDemo { public static void main(String[] args) {MoreParameterNoReturn moreParameterNoReturn = (int a, int b) -> { System.out.println("多参数无返回值:" + "参数1:" + a + "参数2:" + b); }; moreParameterNoReturn.action(20, 30); }}
例4(无参数有返回值):
@FunctionalInterfaceinterface NoParameterReturn { int action();}public class TestDemo { public static void main(String[] args) { NoParameterReturn noParameterReturn = () -> { System.out.println("无参数有返回值"); return 40; }; //接收函数的返回值 int ret = noParameterReturn.action(); System.out.println(ret); }}
例5(一参数有返回值):
//有返回值一个参数@FunctionalInterfaceinterface OneParameterReturn { int action(int a);}public class TestDemo { public static void main(String[] args) { OneParameterReturn oneParameterReturn = (int a) -> { System.out.println("一参数有返回值"); return a; }; ret = oneParameterReturn.action(50); System.out.println(ret); }}
例6(多参数有返回值):
@FunctionalInterfaceinterface MoreParameterReturn { int action(int a, int b);} public class TestDemo { public static void main(String[] args) { MoreParameterReturn moreParameterReturn = (int a, int b) -> { System.out.println("多参数有返回值"); return a + b; }; ret = moreParameterReturn.action(60, 70); System.out.println(ret); }}
3.2、Lambda表达式的语法精简
Lambda 表达式的语法精简:
- 参数类型可以省略,如果需要省略,每个参数的类型都要省略;
- 参数的小括号里面只有一个参数,那么小括号可以省略;
- 如果方法体当中只有一句代码,那么大括号可以省略;
- 如果方法体中只有一条语句,其是 return 语句,那么大括号可以省略,且去掉 return 关键字。
4、变量捕获
Lambda 表达式中存在变量捕获,了解了变量捕获之后,我们才能更好的理解 Lambda 表达式的作用域。Java 当中的匿名类中,会存在变量捕获。
4.1、匿名内部类的变量捕获
所谓变量捕获就是:匿名内部类中访问所在方法或代码块中的局部变量,这个过程被称为 “变量捕获”。
public static void main(String[] args) { // 变量捕获 int a = 100; NoParameterNoReturn noParameterNoReturn = new NoParameterNoReturn() { @Override public void action() { // 捕获a变量 System.out.println(a); } }; noParameterNoReturn.action();}结果:100
但是,只有在局部变量是 “有效的最终变量” 时,匿名内部类才能够引用它。如果局部变量不是有效的最终变量,则无法在匿名内部类中引用它,会导致编译错误。
有效的最终变量指的是一个在生命周期中没有被修改过的局部变量,它可以被认为是一个常量。
4.2、Lambda的变量捕获
同样的,Lambda 的变量捕获也是只有在局部变量是 “有效的最终变量” 时,才能够引用它。
public static void main(String[] args) { // 变量捕获 int a = 100; NoParameterNoReturn noParameterNoReturn = ()->{ // 捕获a变量 System.out.println(a); }; noParameterNoReturn.action();}结果:100
4.3、匿名内部类的变量捕获与Lambda的变量捕获的区别
区别:Lambda 表达式可以捕获外面的 this,而匿名内部类无法直接捕获外面的 this。
在 Lambda 表达式中,this 关键字引用的是 Lambda 表达式所在的类的实例,而不是 Lambda 表达式内部的类的实例。这个特性被称为 “闭包”。
匿名内部类中的 this 关键字引用的是匿名内部类的实例,而不是外部类的实例。
public class Student { // 外部类的属性 String name = "Student"; int age; NoParameterNoReturn noParameterNoReturn = new NoParameterNoReturn() { // 内部类自己的属性 String name = "hello"; @Override public void action() { System.out.println(this.name); } }; NoParameterNoReturn noParameterNoReturn2 = ()->{ String name = "hello"; System.out.println(this.name); }; public static void main(String[] args) { Student student = new Student(); // 结果:hello student.noParameterNoReturn.action(); // 结果:Student student.noParameterNoReturn2.action(); }}
如果想让匿名内部类的捕获外面的 this,就需要使用外部类的类名或对象引用来访问。
public class Student { // 外部类的属性 String name = "Student"; int age; NoParameterNoReturn noParameterNoReturn = new NoParameterNoReturn() { // 内部类自己的属性 String name = "hello"; @Override public void action() { System.out.println(Student.this.name); } };}结果:Student
5、Lambda 在集合当中的使用
为了能够让 Lambda 和 Java 的集合类集更好的一起使用,集合当中,也新增了部分接口,以便与 Lambda 表达式对接。
对应的接口 | 新增的方法 |
---|---|
Collection | forEach() removeIf() spliterator() stream() parallelStream() |
List | replaceAll() sort() |
Map | getOrDefault() forEach() replaceAll() putIfAbsent() remove() replace() computeIfAbsent() computeIfPresent() compute() merge() |
5.1、Collection接口中的forEach()方法
这个方法是在接口 Iterable 当中的:
default void forEach(Consumer super T> action) { Objects.requireNonNull(action); for (T t : this) { action.accept(t); } }
这个方法表示用于对集合中的每个元素执行指定的操作。
java复制代码public class Test { public static void main(String[] args) { List list = new ArrayList<>(); list.add("Hello"); list.add("bit"); list.add("hello"); list.add("lambda"); // Lambda 遍历 list list.forEach((String s)->{ System.out.println(s); }); // 简化 // list.forEach(s -> System.out.println(s)); }}结果:Hello bit hello lambda
5.2、List接口中的sort()方法
List 接口中的 sort() 方法:
default void sort(Comparator super E> c) { Object[] a = this.toArray(); Arrays.sort(a, (Comparator) c); ListIterator i = this.listIterator(); for (Object e : a) { i.next(); i.set((E) e); } }
该方法根据 c 指定的比较规则对容器元素进行排序。可以看到其参数是 Comparator,我们点进去看下:又是一个函数式接口:
@FunctionalInterfacepublic interface Comparator {... int compare(T o1, T o2); ...}
使用示例:
public class TestDemo2 { public static void main(String[] args) { ArrayList list = new ArrayList<>(); list.add("Hello"); list.add("bit"); list.add("hello"); list.add("lambda"); list.sort(new Comparator() { @Override public int compare(String o1, String o2) { return o1.length() - o2.length(); } }); list.forEach(s -> System.out.println(s)); }}输出结果为:bit Hello hello lambda
修改为 Lambda 表达式:
public class TestDemo2 { public static void main(String[] args) { ArrayList list = new ArrayList<>(); list.add("Hello"); list.add("bit"); list.add("hello"); list.add("lambda"); // 对list集合中的字符串按照长度进行排序 list.sort((o1, o2) -> o1.length() - o2.length()); list.forEach(s -> System.out.println(s)); }}输出结果为:bit Hello hello lambda
5.3、Map接口的forEach()方法
HashMap 的 forEach() 方法:
default void forEach(BiConsumer super K, ? super V> action) { Objects.requireNonNull(action); for (Map.Entry entry : entrySet()) { K k; V v; try { k = entry.getKey(); v = entry.getValue(); } catch(IllegalStateException ise) { // this usually means the entry is no longer in the map. throw new ConcurrentModificationException(ise); } action.accept(k, v); } }
可以看到这个方法有一个 BigConsumer,并且这也是一个函数式接口:
@FunctionalInterfacepublic interface BiConsumer {... void accept(T t, U u); ...}
代码示例:
public class TestDemo2 { public static void main(String[] args) { HashMap map = new HashMap<>(); map.put(1, "hello"); map.put(2, "bit"); map.put(3, "hello"); map.put(4, "lambda"); map.forEach(new BiConsumer() { @Override public void accept(Integer integer, String string) { System.out.println(integer + " " + string); } }); }}输出结果:1 hello2 bit3 hello4 lambda
修改为 Lambda 表达式:
public class TestDemo2 { public static void main(String[] args) { HashMap map = new HashMap<>(); map.put(1, "hello"); map.put(2, "bit"); map.put(3, "hello"); map.put(4, "lambda"); map.forEach((integer, string) -> System.out.println(integer + " " + string) ); }}
来源地址:https://blog.csdn.net/weixin_45187434/article/details/131622541
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341