基本用法
Lambda表达式本质:用作接口的实现(其实就是把之前实现接口的步骤简化了)。接口必须是函数式接口
一句话说明函数式接口:接口只有一个抽象方法
函数式接口可以使用@FunctionalInterface
注解进行校验,非函数式接口使用此注解会报错
-> :Lambda操作符
->的左边:Lambda形参列表(即抽象方法中的形参列表)
->的右边:Lambda体(即重写后的抽象方法体)
格式:
1 2 3 4 5 6 7
| <函数式接口> <变量名> = (参数1, 参数2...) -> { };
<函数式接口> <变量名> = (参数1, 参数2...) -> 表达式;
|
参数个数可为0至n个。多个参数需要用逗号,
——分割。
当参数个数为1时,括号可省略;
当参数个数为0时,括号不可省略;
参数前可以不加参数类型(不加会自动推导)。
Lambda 体,可以是一个表达式,也可以是语句块;如果是多条语句,需要使用大括号{}
包裹;如果只有一条语句则可省略大括号{}
,但是必须同时省略return;
表达式中不能加入 return 语句,因为在表达式中已经隐含了 return 语句;但是语句块中没有隐含,需要使用 return 语句;
示例1:只有一个参数,一条语句
注:Consumer是java.util
包下的一个函数式接口
基本写法(匿名实现函数式接口,并调用):
1 2 3 4 5 6 7
| Consumer<String> consumer = new Consumer<String>() { @Override public void accept(String s) { System.out.println(s); } }; consumer.accept("测试方法一");
|
lambda写法(实现函数式接口,并调用):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| Consumer<String> consumer1 = (String s) -> { System.out.println(s); }; consumer1.accept("测试方法一");
Consumer<String> consumer2 = (s) -> { System.out.println(s); }; consumer2.accept("测试方法三");
Consumer<String> consumer3 = s -> { System.out.println(s); }; consumer3.accept("测试方法三");
Consumer<String> consumer4 = s -> System.out.println(s); consumer4.accept("测试方法四");
|
示例2:只有一条语句,没有参数
1 2 3 4 5 6
| Runnable r1 = new Runnable() { @Override public void run() { System.out.println("123"); } };
|
lambda:
1
| Runnable r2 = () -> System.out.println("abc");
|
示例3:只有一条语句,且为返回语句
普通写法:
1 2 3 4 5 6
| Comparator<Integer> comparator21 = new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return Integer.compare(o1,o2); } };
|
lambda:
1 2
| Comparator<Integer> comparator22 = (o1, o2) -> Integer.compare(o1,o2);
|
示例四:参数个数为多条,语句为多条,且有返回值
基本写法:
1 2 3 4 5 6 7 8 9
| Comparator<Integer> comparator1 = new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { System.out.println("一"); System.out.println("二"); return Integer.compare(o1,o2); } }; System.out.println(comparator1.compare(0,9));
|
lambda:
1 2 3 4 5 6
| Comparator<Integer> comparator2 = (o1, o2) -> { System.out.println("一"); System.out.println("二"); return Integer.compare(o1,o2); }; System.out.println(comparator2.compare(0,9));
|
函数式接口
函数式接口只有一个抽象方法,但是可以有多个非抽象方法的接口(同理,任何接口,有且只有一个抽象方法,那么它就是一个函数式接口)。
我们在自定义函数式接口时,可以使用@FunctionalInterface注解进行约束校验。
自定义函数式接口
示例1:
1 2 3 4 5 6 7 8
| @FunctionalInterface interface FunctionTest{ void testMethod(String name, int age);
default void method2() { } }
|
lambda:
1 2 3 4 5
| @Test public void LambdaTest(){ FunctionTest functionTest = (name, age) -> System.out.println("姓名为:" + name + " | 年龄为:" + age); functionTest.testMethod("ahzoo",18); }
|
示例2:
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
| import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.junit.jupiter.api.Test;
public class MethodTest {
@AllArgsConstructor @NoArgsConstructor @Data class User{ private String name; }
@FunctionalInterface interface UserService{ User getUser(); }
@FunctionalInterface interface UserService2{ User getUser(String name); }
@Test public void userTest(){ UserService userService = User::new; User user = userService.getUser(); System.out.println(user);
UserService2 userService2 = User::new; User user2 = userService2.getUser("ahzoo"); System.out.println(user2); System.out.println(user2.getName()); } }
|
引用方法
使用时机:
当我们不想重写某个匿名内部类的方法时,就可以使用lambda表达式的接口快速指向一个已经被实现的方法。将一个方法赋给一个变量或者作为参数传递给另外一个方法。
前提:
参数数量和类型与接口中定义的一致;
返回值类型与接口中定义的一致(一般也是一个函数式接口类型)。
引用格式:
方法归属者::方法名
静态方法的归属者为类名,普通方法归属者为对象;
方法名后面不能带参数。
常见引用:
对象::实例方法
(例:instanceName::methodName
)
类::静态方法
(例:className::methodName
)
类::实例方法
this::实例方法
super::实例方法
构造器::new
数组::new
示例一:
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
| import java.util.Arrays; import java.util.List;
public class MethodTest { public static void main(String[] args) {
List<String> list = Arrays.asList("777", "999");
list.forEach(s -> new MethodTest().print(s)); list.forEach(new MethodTest()::print);
list.forEach(s -> MethodTest.toTest(s)); list.forEach(MethodTest::toTest);
list.forEach(s -> System.out.println(s)); list.forEach(System.out::println);
}
public void print(String s) { System.out.println("打印:" + s); }
public static void toTest(String s) { System.out.println("静态:"+s); } }
|
示例2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import java.util.function.Supplier; import org.junit.jupiter.api.Test;
public class MethodTest {
@Test public void test() {
Supplier<String> supplier = ()->new String(); System.out.println(supplier);
Supplier<String> supplier2 = String::new; System.out.println(supplier2); } }
|
作用域
在匿名内部类中,如果要引用外部变量,需要先将变量声明为final;
虽然在lambda表达式中并未要求一定要将外部变量声明为final,但其实在表达式中变量已经被隐式的声明为final,是不能对其进行修改的;
在lambda表达式中无法声明和局部变量相同的变量
错误示例:
1 2 3
| int i = 1;
Consumer<String> consumer = s -> System.out.println(i++);
|
错误示例:
1 2
| Consumer<String> consumer = consumer -> System.out.println(consumer);
|
常用场景
其实在前面的示例中都已经写过了,这里进行一个总结
遍历
1 2 3 4 5 6 7 8 9
| List<String> list = Arrays.asList("777", "999");
for(String i : list){ System.out.println(i); }
list.forEach(System.out::println);
|
排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| List<Integer> list = Arrays.asList(999, 333, 777, 111);
list.sort(new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return Integer.compare(o1,o2); } });
list.sort(Integer::compare); System.out.println(list);
list.sort((m, n) -> Integer.compare(n, m)); System.out.println(list);
|
线程
1 2 3 4 5 6 7 8 9 10 11
| Runnable runnable = new Runnable() { @Override public void run() { System.out.println("启动新线程"); } }; new Thread(runnable).start();
new Thread(() -> System.out.println("启动新线程")).start();
|