Java8新特性lambda表达式


一、Lambda表达式是什么?

Lambda表达式是Java 8及以后版本中引入的一种新特性,它提供了一种简洁、函数式的方法来定义匿名函数(没有名称的函数)。其实Lambda表达式也没什么神奇的,它本质上就是一个匿名函数而已。Lambda表达式允许你定义一个小型的功能代码块,可以将其传递给任何需要函数式接口的地方。函数式接口是指只包含一个抽象方法的接口,例如多线程中常用的Runnable接口,该接口只有一个run抽象方法。
在这里插入图片描述

二、Lambda表达式语法

Lambda表达式的语法为:

(parameterList) -> { functionBody }

其中,① parameterList为由逗号分隔的参数列表。若如果只有一个参数,那么圆括号可以省略。如果参数列表为空,也必须保留圆括号。parameterList中参数的数据类型可以不写,JVM可以自行推断出来。② 箭头(->):箭头用来分隔参数列表和Lambda函数体。③ Lambda体(functionBody):可以是一个表达式,也可以是一个语句块。如果Lambda体只有一个表达式,那么这个表达式的结果就是Lambda的返回值,这时可以省略大括号和return关键字。如果Lambda体有多条语句,或者包含return语句,那么在函数体中必须使用大括号。

以下为一些具体例子:

1.无参数

例如:

() -> System.out.println("Hello, World!")

具体例子:
在这里插入图片描述

2.一个参数

例如:

name-> System.out.println("Hello, " + name + "!")

具体例子:
在这里插入图片描述

3.两个参数

例如:

(x, y) -> x + y

具体例子:
在这里插入图片描述

4.返回值注意

如果Lambda表达式中使用了if语句,又需要返回值的话,一定要保证if分支都有返回值,避免出现只有一个分支有返回值的情况,如下所示的代码会报错,在编译阶段,编译器就会提醒我们缺少一个return语句。
在这里插入图片描述

5.作用域

在Lambda表达式的函数体中可以使用外部的变量,如下所示:
在这里插入图片描述

三、方法引用和构造器引用

方法引用和构造器引用的目的是为了简化Lambda表达式,如果你Lambda表达式中的函数体功能恰好和已有的方法一样,那么你就不用再写重复代码了,可以直接引用已有的方法来实现函数是接口中的抽象方法。这里的“引用”,可以理解成对现有已编写代码的借用。

1. 方法引用

Lambda表达式的方法引用语法分为三类:
①对象::对象的实例方法(非静态方法)
② 类::类中的实例方法(非静态方法)
③ 类::静态方法

对于情况①,此时Lambda引用一个对象的实例方法,向函数式接口的抽象方法传递的参数都会传递给该引用方法,我们就以Systom.out这个对象的println实例方法为例,如下所示:
在这里插入图片描述
name -> System.out.println(name)这个Lambda表达式做的事情就是打印name,因为可以直接引用现成的打印方法println。注意System.out::println这个引用的形参个数要和函数式接口的抽象函数一样。传入greet抽象方法的实参相当于传给println函数。
对于情况②,此时Lambda引用一个类的实例方法,假设此时有一个Person类,如下所示:
在这里插入图片描述
此时我们使用一个列表存放一些Person对象,接下来我们想把列表中的Person对象按照其年龄从小到大进行排序,List对象的sort方法的形参是Comparator,是一个函数式接口,如下所示:
在这里插入图片描述
我们可以很容易的想到使用Lambda表达式来作为实参传入:(p1, p2) -> p1.compareByAge(p2).这个Lambda表达式完全可以简化成Person::compareByAge,该引用方法将函数式接口的抽象方法的第一个形参作为方法的实施对象,其余参数作为真正的参数,如下所示:
在这里插入图片描述
情况③,此时Lambda引用一个类的静态方法,在这种情况下,向抽象方法传递的参数都会传递给该引用方法,如下图所示,Math::pow等价于(x,y)->Math.pow(x,y).
在这里插入图片描述
注意,不是所有Lambda表达式都可以使用引用方法简化,必须保证Lambda表达式的功能和已存在的方法完全一样(参数列表,返回值都一样)

想要看懂方法引用,需要先判断它属于①、② 、③ 哪种类型的,它是Lambda表达式的简化,因此需要通过它反过来推导它对应的Lambda表达式,就可以理解它到底是什么意思了。

2. 构造器引用

构造器引用和方法引用很像,只不过构造器引用的方法名一定为new,例如Person::new,你一定会问,万一Person类有多个构造器,Person::new调用哪个构造器呢?记住一点,构造器引用不过是Lambda表达式的简化(当然两者还有一些细微区别),而Lambda表达式的目的是实现函数式接口中的抽象方法,那么构造器引用自然也是要实现函数式接口的抽象方法,因此Person::new到底调用无参构造器,还是一个参数的构造器,还是两个参数的构造器,取决于它实现的那个函数式接口的抽象方法的形参个数是0个,1个还是两个。假设如下一个Person类,他有一个无参的构造器和一个两参数的构造器:
在这里插入图片描述
Person::new到底调用哪个构造器取决于函数式接口的抽象方法中的形参列表,如下所示:
在这里插入图片描述


四、Lambda表达式的使用案例

Java8中的许多内置库中的方法的形参都是函数式接口(Runnable, Comparator, Function, Consumer, Predicate),因此可以使用匿名函数作为这些函数式接口变量的实参。

1. 对列表中的元素进行过滤

观察到filter的形参是Predicate,他是一个函数式接口,如下所示
在这里插入图片描述
此时,我们想过滤出以J字母开头的字符串,那么使用filter(name -> name.startsWith("J"))这个匿名函数作为Predicate接口变量的参数即可,如下所示:
在这里插入图片描述

2. 对自定义类组成的列表进行排序

列表中的是自定义的Person类,如果想对该列表中的Person对象按照年龄进行排序,可以使用Lambda表达式。
自定义Person类为:
在这里插入图片描述
观察到List的sort方法的形参是Comparator,如下所示,这是一个函数式接口
在这里插入图片描述
可以使用Lambda表达式作为sort的实参传给sort,如下所示,将Person对象按照年龄从小到大进行了排序。
在这里插入图片描述


五、为什么需要Lambda表达式

在说为什么需要Lambda表达式之前,先介绍另外一个的知识点:内部类。

1. 静态内部类

静态内部类是使用static关键字定义的。静态内部类只能访问外部类的静态成员。静态内部类可以像普通类一样被实例化,而不需要外部类的实例,如下所示:
在这里插入图片描述

2. 实例内部类

实例内部类是最常见的内部类形式。实例内部类可以访问外部类的所有成员(包括私有成员)。要实例化实例内部类,必须先实例化外部类。
在这里插入图片描述

3. 局部内部类

局部内部类定义在方法或代码块中。局部内部类可以访问定义它的方法或代码块中的所有局部变量(包括final和非final的)。
在这里插入图片描述

4. 匿名内部类

匿名内部类是没有名字的类,它主要用于一次性的简单对象创建。通常用于实现接口或抽象类。
在这里插入图片描述
通过上图我们发现,匿名内部类可以实现普通接口中的抽象方法(可不止一个),而Lambda表达式只能实现函数式接口中的抽象方法(只有一个)。但是Lambda表达式确实比匿名内部类的代码更少更简洁。


总结

Lambda表达式用于定义匿名函数,与普通的函数不同,该匿名函数没有名字,仅有函数的形参名字和函数体,其形式上满足(parameterList) -> { functionBody }的结构。Lambda匿名函数用于实现函数式接口中的抽象方法,通过把Lambda匿名函数赋值给函数式接口变量即可,无需像JDK1.8以前一样使用匿名内部类,使得代码非常简洁。

  • 27
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值