JDK8新特性之Lambda表达式、函数式接口
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类名引用静态方法
//定义一个函数式接口@FunctionalInterfaceinterface 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表达式和类名引用静态方法
//定义一个函数式接口@FunctionalInterfaceinterface 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对象名引用方法
代码示例
//定义一个函数式接口@FunctionalInterfaceinterface 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构造器引用方法
看代码示例
//定义一个函数式接口@FunctionalInterfaceinterface 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 类名引用普通方法
看代码示例
//定义一个函数式接口@FunctionalInterfaceinterface 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); }}
运行结果