Lambda表达式

Lambda表达式的格式
- 格式:(形式参数) ->
- 形式参数:如果有多个参数,参数之间用逗号隔开;如果没有参数,留空即可
- ->:有英文中划线和大于符号组成,固定写法。代表指向动作
- 代码块:是我们具体要做的事情,也就是以前我们写的方法体内容
Lambda表达式的使用前提:
- 有一个接口
- 接口中有且仅有一个抽象方法
练习演示:
- 定义一个接口(Addable),里面定义一个抽象方法:int add(int x, int y);
- 定义一个测试类(AddableDemo),在测试类中提供两个方法
- 一个方法是:useAddable(Addable a)
- 一个方法是主方法(main),在主方法中调用useAddable方法
// 接口
public interface Addable{
int add(int x, int y);
}
// 测试类
public class AddableDemo{
public static void main(String[] args){
useAddable((int a, int b) -> {
return x+y;
});
}
public static void useAddable(Addable a){
int sum = a.add(10,20);
System.out.println(sum);
}
}
// 控制台输出结果
30
Lambda表达式的省略模式
省略规则:
- 参数类型可以省略。但是有多个参数的情况下,不能只省略一个
- 如果参数有且仅有一个,那么小括号可以省略
- 如果代码块的语句只有一条,可以省略大括号和分号,甚至是return关键字
// 接口
public interface Flyable{
void fly(String s);
}
// 测试类
public class AddableDemo{
public static void main(String[] args){
useFlyable((String s) -> {
System.out.println(s);
});
// 参数类型省略
useFlyable((s) -> {
System.out.println(s);
});
// 小括号可以省略(参数有且仅有一个)
useFlyable(s -> {
System.out.println(s);
});
// 大括号和分号可以省略(代码块的语句仅有一条--如果有return关键字,则如果省略大括号和分号return关键字也要一起省略掉)
useFlyable(s -> System.out.println(s));
}
public static void useFlyable(Flyable f){
f.fly("风和日丽,晴空万里");
}
}
// 控制台输出结果
风和日丽,晴空万里
风和日丽,晴空万里
风和日丽,晴空万里
风和日丽,晴空万里
Lambda表达式的注意事项

Lambda表达式和匿名内部类的区别
1.所需类型不同
- 匿名内部类:可以是接口,也可以是抽象类,还可以是具体类
- Lambda表达式:只能是接口
2.使用限制不同
- 如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类
- 如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda
3.实现原理不同
- 匿名内部类:编译之后,产生一个单独的.class字节码文件
- Lambda表达式:编译之后,没有一个单独的.class字节码文件。对应的字节码会在运行的时候动态生成
方法引用符
- :: 该符号为引用运算符,而它所在的表达式被称为方法引用
举例:
- Lambda表达式:usePrintable(s -> System.out.println(s));
- 分析:拿到参数s之后通过lambda表达式,传递给System.out.println方法去处理
- 方法引用:usePrintable(System.out::println);
- 分析:直接使用System.out中的println方法来取代Lambda表达式,代码更加的简洁
推导与省略
- 如果使用Lambda。那么根据“可推导就是可省略”的原则,无需指定参数类型,也无需指定重载形式,它们都将被自动推导
- 如果使用方法引用,也是同样可以根据上下文进行推导
- 方法引用是Lambda的孪生兄弟
// 接口
public interface Printable{
void printInt(int i);
}
// 测试类
public class PrintableDemo{
public static void main(String[] args){
// Lambda表达式
usePrintable(i -> System.out.println(i));
// 方法引用
usePrintable(System.out::println);
}
public static void usePrintable(Printable p){
p.printInt(666);
}
}
// 控制台输出结果
666
666
Lambda表达式支持的方法引用
常见的引用方式:
- 引用类方法
- 引用对象的实例方法
- 引用类的实例方法
- 引用构造器
1.引用类方法
引用类方法,其实就是引用类的静态方法
- 格式:类名::静态方法
- 范例:Integer::parseInt
- Integer类的方法:public static int parseInt(String s)将此字符串转换为int类型的数据
// 接口
public interface Converter{
int converter(String s);
}
// 测试类
public class ConverterDemo{
public static void main(String[] args){
// Lambda表达式
useConverter(s -> Integer.parseInt(s));
// 引用类方法
useConverter(Integer::parseInt);
}
public static void useConverter(Converter c){
int num = c.converter("666");
System.out.println(num);
}
}
// 控制台输出结果
666
666
2.引用对象的实例方法
引用对象的实例方法,其实就是引用类中的成员方法
- 格式:对象::成员方法
- 范例:"HelloWorld"::toUpperCase
- String类中的方法:public String toUpperCase()将此String所有字符转换为大写
// 类
public class PrintString{
// 把字符串参数变成大写的数据输出到控制台
public void printUpper(String s){
String result = s.toUpperCase();
System.out.println(result);
}
}
// 接口
public interface Printer{
void printUpperCase(String s);
}
// 测试类
public class PrinterDemo{
public static void main(String[] args){
// Lambda表达式
usePrinter((String s) -> {
String result = s.toUpperCase();
System.out.println(result);
});
// 引用对象的实例方法
PrintString ps = new PrintString();
usePrinter(ps::printUpper);
}
public static void usePrinter(Printer p){
p.printUpperCase("HelloWorld");
}
}
// 控制台输出结果
HELLOWORLD
HELLOWORLD
3.引用类的实例方法
引用类的实例方法,其实就是引用类中的成员方法
- 格式:类名::成员方法
- 范例:String::substring
- String类中的方法:public String substring(int beginIndex, int endIndex)
- 从beginIndex开始到endIndex结束,截取字符串。返回一个子字符串,子字符串的长度为(endIndex - beginIndex)
// 接口
public interface MyString{
String mySubString(String s, int x, int y);
}
// 测试类
public class MyStringDemo{
public static void main(String[] args){
// Lambda表达式
useMyString((String s, int x, int y) -> {
return s.substring(x,y);
});
// 引用类的实例方法
useMyString(String::substring);
}
public static void useMyString(MyString my){
String s = my.mySubString("HelloWorld",2,5);
System.out.println(s);
}
}
// 控制台输出结果
llo
llo
4.引用构造器
引用构造器,其实就是引用构造方法
- 格式:类名::new
- 范例:Student::new
// Student类
public class Student{
private String name;
private int age;
public Student(){}
public Student(String name, int age){
this.name = name;
this.age = age;
}
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
public int getAge(){
return age;
}
public void setAge(int age){
this.age = age;
}
}
// 接口
public interface StudentBuilder{
Student build(String name, int age);
}
// 测试类
public class StudentDemo{
public static void main(String[] args){
// Lambda表达式
useStudentBuilder((String name, int age) -> {
Student s = new Student(name,age);
return s;
});
// 引用构造器
useStudentBuilder(Student::new);
}
public static void useStudentBuilder(StudentBuilder sb){
Student s = sb.build("小明",18);
System.out.println(s.getName()+","+s.getAge());
}
}
// 控制台输出结果
小明,18
小明,18
函数式接口
函数式接口:有且仅有一个抽象方法的接口
Java中函数式编程体现的就是Lambda表达式,所以函数式接口就是可以适用于Lambda所使用的接口。
只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利地进行推导
- @FunctionalInterface 注解
- 放在接口定义的上方:如果接口是函数式接口,编译通过;如果不是,编译失败
- 我们自己定义函数式接口的时候,@FunctionalInterface注解是可选的,就算我们不写这个注解,只要保证满足函数式接口定义的条件,也照样是函数式接口。但是,建议加上该注解
常见的函数式接口:
- Supplier接口
- Consumer接口
- Predicate接口
- Function接口
1.Supplier接口
Supplier
- T get():获得结果
- 该方法不需要参数,它会按照某种实现逻辑(由Lambda表达式实现)返回一个数据
- Supplier
接口也被称为生产型接口,如果我们指定了接口的泛型是什么类型,那么接口中的get方法就生产什么类型的数据供我们使用
练习演示:
- 定义一个类(SupplierTest),在类中提供两个方法
- 一个方法是:int getMax(Supplier
sup) 用于返回一个int数组中的最大值 - 一个方法是主方法main,在主方法中调用getMax方法
- 一个方法是:int getMax(Supplier
public class SupplierTest{
public static void main(String[] args){
// 定义一个int数组
int[] arr = {19, 50, 28, 37, 46};
// 调用方法--参数通过Lambda表达式传递
int maxValue = getMax(() -> {
int max = arr[0];
for(int i=1;i<arr.length;i++){
if(arr[i]>max){
max = arr[i];
}
}
return max;
});
System.out.println(maxValue);
}
// 定义一个方法,返回一个int数组中的最大值
private static int getMax(Supplier<Integer> sup){
return sup.get();
}
}
// 控制台输出结果
50
2.Consumer接口
Consumer
- void accept(T t):对给定的参数执行操作
- default Consumer
andThen(Consumer after):返回一个组合的Consumer,依次执行此操作,然后执行after操作 - Consumer
接口也被称为消费型接口,它消费的数据的数据类型由泛型指定
练习演示:
- String[] strArray = {"小明,18","小兰,19","小茹,20"};
- 字符串数组中有多条信息,请按照格式:"姓名:XX,年龄:XX"的格式将信息打印出来
- 要求:
- 把打印姓名的动作作为第一个Consumer接口的Lambda实例
- 把打印年龄的动作作为第二个Consumer接口的Lambda实例
- 将两个Consumer接口按照顺序组合到一起使用
public class ConsumerTest{
public static void main(String[] args){
// 定义一个字符串数组
String[] strArray = {"小明,18","小兰,19","小茹,20"};
// 调用方法--参数通过Lambda表达式传递
printInfo(strArray,(String str) -> {
String name = str.split(",")[0];
System.out.print("姓名:"+name);
},(String str) -> {
int age = Integer.parseInt(str.split(",")[1]);
System.out.println(",年龄:"+age);
});
System.out.println("----------------");
// 优化代码版
printInfo(strArray,str -> System.out.print("姓名:"+str.split(",")[0]),str -> System.out.println(",年龄:"+Integer.parseInt(str.split(",")[1])));
}
private static void printInfo(String[] strArray, Consumer<String> con1, Consumer<String> con2){
for(String str : strArray){
con1.andThen(con2).accept(str);
}
}
}
// 控制台输出结果
姓名:小明,年龄:18
姓名:小兰,年龄:19
姓名:小茹,年龄:20
----------------
姓名:小明,年龄:18
姓名:小兰,年龄:19
姓名:小茹,年龄:20
3.Predicate接口
Predicate
- boolean test(T t):对给定的参数进行判断(判断逻辑有Lambda表达式实现),返回一个布尔值
- default Predicate
negate():返回一个逻辑的否定,对应逻辑非 ! - default Predicate
and(Predicate other):返回一个组合判断,对应短路与 && - default Predicate
or(Predicate other):返回一个组合判断,对应短路或 || - Predicate
接口通常用于判断参数是否满足指定的条件
练习演示:
- String[] strArray = {"小明,18","小兰,19","小茹,20","小琪琪,34","小雯雯,35"};
- 字符串数组中有多条信息,请通过Predicate接口的拼装将符合要求的字符串筛选到集合ArrayList中,并遍历ArrayList集合
- 需要同时满足如下要求:
- 姓名长度大于2
- 年龄大于33
- 分析:
- 有两个判断条件,所以需要使用两个Predicate接口,对条件进行判断
- 必须同时满足两个条件,所以可以使用and()方法连接两个判断条件
public class PredicateTest{
public static void main(String[] args){
// 定义一个字符串数组
String[] strArray = {"小明,18","小兰,19","小茹,20","小琪琪,34","小雯雯,35"};
// 调用方法--参数通过Lambda表达式传递
ArrayList<String> array1 = myFilter(strArray,(String str) -> {
String name = str.split(",")[0];
if(name.length()>2){
return true;
}
return false;
},(String str) -> {
int age = Integer.parseInt(str.split(",")[1]);
if(age>33){
return true;
}
return false;
});
// 遍历符合条件存入的集合
for(String str : array1){
System.out.println(str);
}
System.out.println("--------");
// 优化代码版
ArrayList<String> array2 = myFilter(strArray,str -> str.split(",")[0].length()>2,str -> Integer.parseInt(str.split(",")[1])>33);
// 遍历符合条件存入的集合
for(String str : array2){
System.out.println(str);
}
}
// 通过Predicate接口的拼装将符合要求的字符串筛选到集合ArrayList中,并遍历ArrayList集合
private static ArrayList<String> myFilter(String[] strArray, Predicate<String> pre1, Predicate<String> pre2){
// 定义一个集合
ArrayList<String> array = new ArrayList<>();
// 遍历集合
for(String str : strArray){
// 判断集合是否满足两个条件
boolean isSuccess = pre1.and(pre2).test(str);
// 当两个条件都为true时,添加到新的集合中等待遍历输出
if(isSuccess){
array.add(str);
}
}
return array;
}
}
// 控制台输出结果
小琪琪,34
小雯雯,35
--------
小琪琪,34
小雯雯,35
4.Function接口
Function<T,R> :常用的两个方法
- R apply(T t):将此函数应用于给定的参数
- default
Function andThen(Function after):返回一个组合函数,首先将该函数应用于输入,然后将after函数应用于结果 - Function<T,R> 接口通常用于对参数进行处理,转换(处理逻辑由Lambda表达式实现),然后返回一个新的值
练习演示:
- String s = "小明,30";
- 请按照指定的要求进行操作:
- 将字符串截取得到数字年龄部分
- 将上一步的年龄字符串转换成int类型的数据
- 将上一步的int数据加70,得到一个int类型的结果,在控制台输出
- 通过Function接口来实现函数的拼接
public class FunctionTest{
public static void main(String[] args){
String s = "小明,30";
convert(s,(String s) -> {
return s.split(",")[1];
},(String ss) -> {
return Integer.parseInt(ss);
},(Integer i) -> {
return i+70;
})
System.out.println("--------");
// 优化代码版1
convert(s,s -> s.split(",")[1],ss -> Integer.parseInt(ss),i -> i+70);
System.out.println("--------");
// 优化代码版2
convert(s,s -> s.split(",")[1],Integer::parseInt,i -> i+70);
}
private static void convert(String s, Function<String,String> fun1, Function<String,Integer> fun2, Function<Integer,Integer> fun3){
int i = fun1.andThen(fun2).andThen(fun3).apply(s);
System.out.println(i);
}
}
// 控制台输出结果
100
--------
100
--------
100
本文来自博客园,作者:早晨9点,转载请注明原文链接:https://www.cnblogs.com/onesun/p/15811338.html

浙公网安备 33010602011771号