lambda表达式概述
AKA 函数式编程。只有一个方法的类。方法即可代表类。
简介 lambda运行将函数作为一个方法的参数,也就是函数作为参数传递到方法中。使用lambda表达式可以让代码更加简洁。
Lambda表达式的使用场景:用以简化接口实现。
接口实现
1 2 3 4 5 Interface B{} Class A implements B{ } new A();
1 2 3 4 5 //直接创建匿名内部类。 Interface B{} new B(){ }
1 2 3 4 5 6 //使用lambda表达式实现接口 Interface B{} Test test = () -> { System.out.println("test"); }; test.test();
注意事项 这⾥类似于局部内部类、匿名内部类,依然存在闭包的问题。
如果在lambda表达式中,使用到了局部变量,那么这个局部变量会被隐式的声明为 final。是⼀个常量,不能修改值。
2 函数式接口 函数式接口 lambda表达式,只能实现函数式接口。
函数式接口:如果说,⼀个接口中,要求实现类必须实现的抽象方法,有且只有⼀个!
1 2 3 4 interface Test { public void test () ; }
lambda表达式毕竟只是⼀个匿名方法。当实现的接口中的方法过多或者多少的时候,lambda表达式都是不适用的。
@FunctionalInterface
是⼀个注解,用在接口之前,判断这个接口是否是⼀个函数式接口。 如果是函数式接口,没有任何问题。如果不是函数式接口,则会报错。功能类似于 @Override。
1 2 3 4 @FunctionalInterface interface Test { public void test () ; }
3 Lambda表达式的语法 使用lambda表带是实现一个函数式接口
基础语法 不需要关注参数类型或者返回值类型。
参数部分:方法的参数列表,要求和实现的接口中的方法参数部分⼀致,包括参数的数量和类型。
方法体部分 : 方法的实现部分,如果接口中定义的方法有返回值,则在实现的时候,注意返回值的返回。
-> : 分隔参数部分和方法体部分。
1 2 3 4 5 6 7 8 9 10 // 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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package test;public class Test04 { public static void main (String[] args) { Test test = (name,age) -> { System.out.println(name+age+"岁了!" ); return age + 1 ; }; int age = test.test("小新" ,18 ); System.out.println(age); } } interface Test { public int test (String name,int age) ; }
语法进阶
参数的类型可以省略不写。由于在接口的方法中,已经定义了每⼀个参数的类型是什么。而且在使用lambda表达式实现接口的时候,必须要保证参数的数量和类 型需要和接口中的方法保持⼀致。要省略, 每⼀个参数的类型都必须省略不写。绝对不能出现,有的参数类型省略了,有的参数类型没有省略。
1 2 3 4 5 6 7 Test test = (name,age) -> { System.out.println(name+age+"岁了!" ); return age + 1 ; }; int age = test.test("小新" ,18 ); System.out.println(age);
参数的小括号可以省略不写。如果方法的参数列表中的参数数量 有且只有⼀个,此时,参数列表的小括号是可以省略不写的。
1 2 3 4 5 Test test = name -> { System.out.println(name+"test" ); }; test.test("小新" );
return可以省略不写。如果⼀个方法中唯⼀的⼀条语句是⼀个返回语句, 此时在省略掉大括号的同时, 也必须省略掉return。
1 Test test = (a,b) -> a+b;
4 函数引用 函数引用的概念 lambda表达式是为了简化接口的实现的。在lambda表达式中,不应该出现比较复杂的逻辑。如果在lambda表达式中出现了过于复杂的逻辑,会对程序的可读性造成非常大的影响。如果在lambda表达式中需要处理的逻辑比较复杂,⼀般情况会单独的写⼀个方法。在lambda表达式中直接引用这个方法即可
函数引用:引用⼀个已经存在的方法,使其替代lambda表达式完成接口的实现
与lambda表达式的不同 lambda和方法引用之间有一个关键的区别:
Lambda 是惰性的,它们只会在调用方法时调用类构造函数。
方法引用,构造函数只会在分配了方法引用的地方被立即调用,而不是在方法调用时调用。
对象方法引用,对象必须已经初始化。类方法引用,类也一定会进行初始化。例如
1 Runnable universeImpactRunnable = () -> new ChuckNorris ().roundHouseKick();
1 Runnable galaxyImpactRunnable = new ChuckNorris()::roundHouseKick;
静态方法的引用
引用方法后面,不要添加小括号。
引用方法、参数和返回值,必须要跟接口中定义的一致。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package test;public class Test05 { public static void main (String[] args) { Test1 test1 = Calculator::calculate; System.out.println(test1.test(4 ,5 )); } } class Calculator { public static int calculate (int a,int b ) { if (a > b) { return a - b; } return b - a; } } interface Test1 { int test (int a,int b) ; }
非静态方法的引用
在引用的方法后⾯,不要添加小括号。
引用的这个方法, 参数(数量、类型) 和 返回值, 必须要跟接口中定义的⼀致。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package test; /** * @author: Mercury * Date: 2022/3/21 * Time: 8:14 * Description:lambda表达式对非静态方法的引用 * Version:1.0 */ public class Test06 { public static void main(String[] args) { //对非静态方法的引用,需要使用对象来完成 Test2 test2 = new Calculator()::calculate; System.out.println(test2.calculate(2, 3)); } private static class Calculator{ public int calculate(int a, int b) { return a > b ? a - b : b - a; } } } interface Test2{ int calculate(int a,int b); }
构造方法的引用 使用场景
如果某⼀个函数式接口中定义的方法,仅仅是为了得到⼀个类的对象。此时我们就可以使用构造方法的引用,简化这个方法的实现。
注意事项:可以通过接口中的方法的参数, 区分引用不同的构造方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 package com.cq.test;public class Test { private static class Dog { String name; int age; public Dog () { System.out.println("一个Dog对象通过无参构造被实例化了" ); } public Dog (String name,int age) { System.out.println("一个Dog对象通过有参构造被实例化了" ); this .name = name; this .age = age; } } @FunctionalInterface private interface GetDog { Dog test () ; } @FunctionalInterface private interface GetDogWithParameter { Dog test (String name,int age) ; } public static void main (String[] args) { GetDog lm = Dog::new ; Dog dog = lm.test(); System.out.println("修狗的名字:" +dog.name+" 修狗的年龄:" +dog.age); GetDogWithParameter lm2 = Dog::new ; Dog dog1 = lm2.test("萨摩耶" ,2 ); System.out.println("修狗的名字:" +dog1.name+" 修狗的年龄:" +dog1.age); } }
4 集合中的使用 forEach( )方法演示: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public static void main (String[] args) { ArrayList<String>list=new ArrayList <>(); list.add("a" ); list.add("bc" ); list.add("def" ); list.add("hello" ); list.forEach(new Consumer <String>() { @Override public void accept (String s) { System.out.println(s); } }); list.forEach(s-> System.out.println(s)); }
sort()方法的演示 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public static void main (String[] args) { ArrayList<String>list=new ArrayList <>(); list.add("hh" ); list.add("hi" ); list.add("def" ); list.add("abc" ); list.sort(new Comparator <String>() { @Override public int compare (String o1,String o2) { return o1.compareTo(o2); } }); list.forEach(s-> System.out.println(s)); System.out.println("======分割线======" ); list.sort(((o1, o2) -> o1.compareTo(o2))); list.forEach(s-> System.out.println(s)); }
HashMap 的 forEach() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public static void main(String[] args) { HashMap<Integer,String>map=new HashMap<>(); map.put(1,"hello"); map.put(2,"I"); map.put(3,"love"); map.put(4,"china"); //法一:(不用Lambda) map.forEach(new BiConsumer<Integer, String>() { @Override public void accept(Integer integer, String s) { System.out.println("key:"+integer+"value:"+s); } }); System.out.println("======分割线======"); //法二:(用Lambda) map.forEach((key,value)-> System.out.println("key:"+key+"value:"+value)); }
5 jdk中的函数式接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @FunctionalInterface public interface Runnable { public abstract void run () ; }
jdk中任意地方可以接收该接口的对象。是一个标准的无参、无返回值的函数。传入任何无参无返回值的函数即可执行。