JDK8特性之Lamdba、函数式接口
1. 什么是Lambda表达式 ❓
Lambda表达式时JDK8的一个重要的特性,它使用一个清晰简介的表达式来表达一个接口,同时Lambda表达式也简化了对接和以及数组数据的遍历、过滤和提取等操作。
⭐️1.1入门🚪
Lambda表达式可以简化匿名内部类。
如果匿名内部类的实现非常简单,例如只包含一个抽象方法的接口,那么匿名内部类的语法仍然显得比较冗余。所以JDK8增加了一个特性Lambda表达式,但是这种表达式只针对有一个抽象方法的接口实现,以简洁的的表达式形式实现接口的功能来作为方法参数
🔥1.2Lambda语法
([数据类型,参数名.......])->{表达式主体}
([数据类型,参数名.......])
:指的是 向表达式主体传递接口方法需要的参数。多个参数中间用 , 隔开。参数的数据类型可以省略,后面的表达式主题会自动进行校对和匹配。- 如果只有一个参数,则可以省略括号
->
:表示Lambda表达式箭牌,用来指定参数数据指向,不能省略,必须是英文横线和大于号书写{ 表达式主体}
:本质就是接口中抽象方法的具体实现- 如果表达式主题只有一条语句,那么可以省略包含主体的大括号
- 在有返回值时,如果只有一条return语句,也可以省略return关键字
👊1.3Lambda表达式实操
//定义接口
interface Animal{
void shout();
}
class A implements Animal{
@Override
public void shout() {
System.out.println("最正常的方式");
}
}
class test{
public static void main(String[] args) {
String name="一键三连";
//使用内名内部类作为参数传递给animalshout方法
animalshout(new Animal() {
@Override
public void shout() {
System.out.println("匿名内部类都会"+name);
}
});
//使用Lambda表达式
animalshout(()-> System.out.println("Lambda表达式也会"+name));
//最正常的方式
A a=new A();
animalshout(a);
}
public static void animalshout(Animal an){
an.shout();
}
}
运行结果:
上面的代码中调用animalshout方法时需要一个Animal接口类型的参数,遇到这种情况我们有三种选择
- 实现这个接口,传入这个接口实现类的对象。也就是我们上面定义的最正常的方式。但是这种方式有个弊端,不能访问name属性,因为name属性是main方法的局部变量。而且写法明显也比较麻烦
- 使用匿名内部类,可以看到这种方式访问了局部变量name,而局部变量并没有使用final修饰,程序也没有报错。
这是JDK8开始有的新特性,允许在局部内部类、匿名内部类中访问非final修饰的局部变量,而在JDK8之前,局部变量前必须加final修饰符,否则程序编译出错 - 使用Lambda表达式,可以看出使用Lambda表达式写出的代码更加简洁和清晰。
2.函数式接口
2.1什么是函数式接口❓
函数式接口是指有且仅有一个抽象方法的接口,Lambda表达式就是Java中的函数式编程的体现,只有确保接口中有且仅有一个抽象方法,Lambda表达式才能顺利的推导出所实现的这个接口中的方法
🔔2.2@FunctionalInterface注解
在JDK8中专门为函数时接口引入了一个@FunctionalInterface注解,该注解只是显示的标注了接口是一个函数式接口,并强制编辑器进行更严格的检查,如果不是函数式接口,那么编译器就会报错,对程序运行并没有实质上的影响。类似于@overwrite注解
📢2.3方法引用和构造器引用
Lambda表达式的主体只有一条语句时,程序不仅可以省略大括号,还可以通过英文 “::”的语法格式来引用方法和构造器(构造方法)。
🔍📚2.3.1Lambda表达式对普通方法和构造方法的引用形式
种类 | Lambda表达式示例 | 对应的引用示例 |
---|---|---|
类名引用普通方法 | (x,y,…)->对象名 x.类普通方法名(y,…) | 类名::类普通方法名 |
类名引用静态方法 | (x,y,…)->类名.类静态方法名(x,y,…) | 类名::类静态方法名 |
对象名引用方法 | (x,y,…)->对象名.实例方法名(x,y,…) | 对象名::实例方法名 |
构造器引用 | (x,y,…)->new类名(x,y,…) | 类名::new |
|
📕2.3.1.1类名引用静态方法
//定义一个函数式接口
@FunctionalInterface
interface Calcable{
int calc(int num);
}
//定义一个类且包含静态方法
class Math{
//静态方法
public static int abs(int num){
if(num<0){
return -num;
}else {
return num;
}
}
}
//定义测试类
class test{
private static void printAbs(int num,Calcable calcable){
System.out.println(calcable.calc(num));
}
public static void main(String[] args) {
//使用lambda表达式
printAbs(-100,n->Math.abs(n));
//使用方法引用方式
printAbs(-200,Math::abs);
}
}
结果
可以看出使用类名引用静态方法的方式更加简洁
其实不论是Lambda表达式还是方法引用 都是帮助我们写的代码更加清晰和简洁,设想一下如果我们没有掌握这两种方法,那我们要完成上面代码的调用应该怎么办?
📗2.3.1.2 不使用Lambda表达式和类名引用静态方法
//定义一个函数式接口
@FunctionalInterface
interface Calcable{
int calc(int num);
}
//定义一个类实现接口并重写方法
class Math implements Calcable{
@Override
public int calc(int num) {
if(num<0){
return -num;
}else {
return num;
}
}
}
//定义测试类
class test{
private static void printAbs(int num,Calcable calcable){
System.out.println(calcable.calc(num));
}
public static void main(String[] args) {
Math math=new Math();
printAbs(50000,math);
}
}
可以看出如果不使用JDK8给我们的新特性的话,我们就需要写一个接口的实现类,并且实现这个方法,然后在调用的时候,还需要实例化实现类。
通过对比可以更加理解新特性带来的方式的实现方法更为简洁。
📘2.3.1.3对象名引用方法
代码示例
//定义一个函数式接口
@FunctionalInterface
interface Calcable{
String calc(int num);
}
class Math{
//静态方法
public String abs(int num){
if(num<0){
return "是负数";
}else {
return "不是负数";
}
}
}
//定义测试类
class test{
private static void printAbs(int num,Calcable calcable){
System.out.println(calcable.calc(num));
}
public static void main(String[] args) {
Math ma=new Math();
//使用lambda表达式
printAbs(-100,n->ma.abs(n));
//使用方法引用方式
printAbs(-200,ma::abs);
}
}
运行结果
还是方法引用更加简洁
📙2.3.1.4构造器引用方法
看代码示例
//定义一个函数式接口
@FunctionalInterface
interface PersonFace{
Person build(String name);
}
//定义Person类,并添加构造方法
class Person{
String name;
public Person(String name){
this.name=name;
}
public String getName(){
return name;
}
}
//定义测试类
class test{
private static void printName(String name,PersonFace personFace){
System.out.println(personFace.build(name).getName());
}
public static void main(String[] args) {
//使用lambda表达式
printName("使用lambda表达式会三连",n->new Person(n));
//使用方法引用方式
printName("使用方法引用方式会三连",Person::new);
}
}
运行结果
📒2.3.1.5 类名引用普通方法
看代码示例
//定义一个函数式接口
@FunctionalInterface
interface Printable {
void print(StringU stringU, String str);
}
//定义Person类,并添加构造方法
class StringU {
public void printUpperCase(String str) {
System.out.println(str.toUpperCase());
}
}
//定义测试类
class test1 {
private static void printUpper(StringU stringUtils, String text,Printable printable) {
printable.print(stringUtils,text);
}
public static void main(String[] args) {
//使用lambda表达式
printUpper(new StringU(),"Java",(object,t)->object.printUpperCase(t));
//使用方法引用方式
printUpper(new StringU(),"Java",StringU::printUpperCase);
}
}
运行结果