Lambda表达式
目录
- Lambda简介
- 对接口的要求
- @FunctionalInterface
- Lambda 基础语法
- Lambda 语法简化
- Lambda 表达式常用示例
- Lambda 表达式中的闭包问题
- default 关键字
- 总结
Lambda简介
Lambda 表达式是 JDK8 的一个新特性,可以取代大部分的匿名内部类,写出更优雅的 Java 代码,尤其在集合的遍历和其他集合操作中,可以极大地优化代码结构。
JDK 也提供了大量的内置函数式接口供我们使用,使得 Lambda 表达式的运用更加方便、高效。
对接口的要求
虽然使用 Lambda 表达式可以对某些接口进行简单的实现,但并不是所有的接口都可以使用 Lambda 表达式来实现。
- 接口有且仅有一个抽象方法 :Lambda 规定接口中只能有一个需要被实现的方法,不是规定接口中只能有一个方法
- 允许定义静态方法 :static
- 允许定义默认方法 :default
- 允许java.lang.Object中的public方法 : equals() 等
- 该注解不是必须的,如果一个接口符合"函数式接口"定义,那么加不加该注解都没有影响。加上该注解能够更好地让编译器进行检查。如果编写的不是函数式接口,但是加上了@FunctionInterface,那么编译器会报错
jdk 8 中有另一个新特性:default, 接口中被 default 修饰的方法会有默认实现,不是必须被实现的方法,所以不影响 Lambda 表达式的使用。
@FunctionalInterface
修饰函数式接口的,要求接口中的抽象方法只有一个。 这个注解往往会和 lambda 表达式一起出现。
Lambda 基础语法
我们这里给出六个接口,后文的全部操作都利用这六个接口来进行阐述。
1 /**多参数无返回*/ 2 @FunctionalInterface 3 public interface NoReturnMultiParam { 4 void method(int a, int b); 5 } 6 7 /**无参无返回值*/ 8 @FunctionalInterface 9 public interface NoReturnNoParam { 10 void method(); 11 } 12 13 /**一个参数无返回*/ 14 @FunctionalInterface 15 public interface NoReturnOneParam { 16 void method(int a); 17 } 18 19 /**多个参数有返回值*/ 20 @FunctionalInterface 21 public interface ReturnMultiParam { 22 int method(int a, int b); 23 } 24 25 /*** 无参有返回*/ 26 @FunctionalInterface 27 public interface ReturnNoParam { 28 int method(); 29 } 30 31 /**一个参数有返回值*/ 32 @FunctionalInterface 33 public interface ReturnOneParam { 34 int method(int a); 35 }
语法形式为 () -> {},其中 () 用来描述参数列表,{} 用来描述方法体,-> 为 lambda运算符 ,读作(goes to)。
1 import lambda.interfaces.*; 2 3 public class Test1 { 4 public static void main(String[] args) { 5 6 //无参无返回 7 NoReturnNoParam noReturnNoParam = () -> { 8 System.out.println("NoReturnNoParam"); 9 }; 10 noReturnNoParam.method(); 11 12 //一个参数无返回 13 NoReturnOneParam noReturnOneParam = (int a) -> { 14 System.out.println("NoReturnOneParam param:" + a); 15 }; 16 noReturnOneParam.method(6); 17 18 //多个参数无返回 19 NoReturnMultiParam noReturnMultiParam = (int a, int b) -> { 20 System.out.println("NoReturnMultiParam param:" + "{" + a +"," + + b +"}"); 21 }; 22 noReturnMultiParam.method(6, 8); 23 24 //无参有返回值 25 ReturnNoParam returnNoParam = () -> { 26 System.out.print("ReturnNoParam"); 27 return 1; 28 }; 29 30 int res = returnNoParam.method(); 31 System.out.println("return:" + res); 32 33 //一个参数有返回值 34 ReturnOneParam returnOneParam = (int a) -> { 35 System.out.println("ReturnOneParam param:" + a); 36 return 1; 37 }; 38 39 int res2 = returnOneParam.method(6); 40 System.out.println("return:" + res2); 41 42 //多个参数有返回值 43 ReturnMultiParam returnMultiParam = (int a, int b) -> { 44 System.out.println("ReturnMultiParam param:" + "{" + a + "," + b +"}"); 45 return 1; 46 }; 47 48 int res3 = returnMultiParam.method(6, 8); 49 System.out.println("return:" + res3); 50 } 51 }
Lambda 语法简化
我们可以通过观察以下代码来完成代码的进一步简化,写出更加优雅的代码。
1 import lambda.interfaces.*; 2 3 public class Test2 { 4 public static void main(String[] args) { 5 6 //1.简化参数类型,可以不写参数类型,但是必须所有参数都不写 7 NoReturnMultiParam lamdba1 = (a, b) -> { 8 System.out.println("简化参数类型"); 9 }; 10 lamdba1.method(1, 2); 11 12 //2.简化参数小括号,如果只有一个参数则可以省略参数小括号 13 NoReturnOneParam lambda2 = a -> { 14 System.out.println("简化参数小括号"); 15 }; 16 lambda2.method(1); 17 18 //3.简化方法体大括号,如果方法条只有一条语句,则可以胜率方法体大括号 19 NoReturnNoParam lambda3 = () -> System.out.println("简化方法体大括号"); 20 lambda3.method(); 21 22 //4.如果方法体只有一条语句,并且是 return 语句,则可以省略方法体大括号 23 ReturnOneParam lambda4 = a -> a+3; 24 System.out.println(lambda4.method(5)); 25 26 ReturnMultiParam lambda5 = (a, b) -> a+b; 27 System.out.println(lambda5.method(1, 1)); 28 } 29 }
Lambda 表达式常用示例
- lambda 表达式引用方法
有时候我们不是必须要自己重写某个匿名内部类的方法,我们可以可以利用 lambda表达式的接口快速指向一个已经被实现的方法。
语法
方法归属者::方法名 静态方法的归属者为类名,普通方法归属者为对象
1 public class Exe1 { 2 public static void main(String[] args) { 3 ReturnOneParam lambda1 = a -> doubleNum(a); 4 System.out.println(lambda1.method(3)); 5 6 //lambda2 引用了已经实现的 doubleNum 方法 7 ReturnOneParam lambda2 = Exe1::doubleNum; 8 System.out.println(lambda2.method(3)); 9 10 Exe1 exe = new Exe1(); 11 12 //lambda4 引用了已经实现的 addTwo 方法 13 ReturnOneParam lambda4 = exe::addTwo; 14 System.out.println(lambda4.method(2)); 15 } 16 17 /** 18 * 要求 19 * 1.参数数量和类型要与接口中定义的一致 20 * 2.返回值类型要与接口中定义的一致 21 */ 22 public static int doubleNum(int a) { 23 return a * 2; 24 } 25 26 public int addTwo(int a) { 27 return a + 2; 28 } 29 }
- 构造方法的引用
一般我们需要声明接口,该接口作为对象的生成器,通过 类名::new 的方式来实例化对象,然后调用方法返回对象。
1 interface ItemCreatorBlankConstruct { 2 Item getItem(); 3 } 4 interface ItemCreatorParamContruct { 5 Item getItem(int id, String name, double price); 6 } 7 8 public class Exe2 { 9 public static void main(String[] args) { 10 ItemCreatorBlankConstruct creator = () -> new Item(); 11 Item item = creator.getItem(); 12 13 ItemCreatorBlankConstruct creator2 = Item::new; 14 Item item2 = creator2.getItem(); 15 16 ItemCreatorParamContruct creator3 = Item::new; 17 Item item3 = creator3.getItem(112, "鼠标", 135.99); 18 } 19 }
- lambda 表达式创建线程
我们以往都是通过创建 Thread 对象,然后通过匿名内部类重写 run() 方法,一提到匿名内部类我们就应该想到可以使用 lambda 表达式来简化线程的创建过程。
1 Thread t = new Thread(() -> { 2 for (int i = 0; i < 10; i++) { 3 System.out.println(2 + ":" + i); 4 } 5 }); 6 t.start();
- 遍历集合
我们可以调用集合的 public void forEach(Consumer<? super E> action) 方法,通过 lambda 表达式的方式遍历集合中的元素。以下是 Consumer 接口的方法以及遍历集合的操作。Consumer 接口是 jdk 为我们提供的一个函数式接口。
1 @FunctionalInterface 2 public interface Consumer<T> { 3 void accept(T t); 4 //.... 5 } 6 ArrayList<Integer> list = new ArrayList<>(); 7 8 Collections.addAll(list, 1,2,3,4,5); 9 10 //lambda表达式 方法引用 11 list.forEach(System.out::println); 12 13 list.forEach(element -> { 14 if (element % 2 == 0) { 15 System.out.println(element); 16 } 17 });
- 删除集合中的某个元素
我们通过public boolean removeIf(Predicate<? super E> filter)方法来删除集合中的某个元素,Predicate 也是 jdk 为我们提供的一个函数式接口,可以简化程序的编写。
1 ArrayList<Item> items = new ArrayList<>(); 2 items.add(new Item(11, "小牙刷", 12.05 )); 3 items.add(new Item(5, "日本马桶盖", 999.05 )); 4 items.add(new Item(7, "格力空调", 888.88 )); 5 items.add(new Item(17, "肥皂", 2.00 )); 6 items.add(new Item(9, "冰箱", 4200.00 )); 7 8 items.removeIf(ele -> ele.getId() == 7); 9 10 //通过 foreach 遍历,查看是否已经删除 11 items.forEach(System.out::println);
- 集合内元素的排序
在以前我们若要为集合内的元素排序,就必须调用 sort 方法,传入比较器匿名内部类重写 compare 方法,我们现在可以使用 lambda 表达式来简化代码。
1 ArrayList<Item> list = new ArrayList<>(); 2 list.add(new Item(13, "背心", 7.80)); 3 list.add(new Item(11, "半袖", 37.80)); 4 list.add(new Item(14, "风衣", 139.80)); 5 list.add(new Item(12, "秋裤", 55.33)); 6 7 /* 8 list.sort(new Comparator<Item>() { 9 @Override 10 public int compare(Item o1, Item o2) { 11 return o1.getId() - o2.getId(); 12 } 13 }); 14 */ 15 16 list.sort((o1, o2) -> o1.getId() - o2.getId()); 17 18 System.out.println(list);
Lambda 表达式中的闭包问题
这个问题我们在匿名内部类中也会存在,如果我们把注释放开会报错,告诉我 num 值是 final 不能被改变。这里我们虽然没有标识 num 类型为 final,但是在编译期间虚拟机会帮我们加上 final 修饰关键字。
1 import java.util.function.Consumer; 2 public class Main { 3 public static void main(String[] args) { 4 5 int num = 10; 6 7 Consumer<String> consumer = ele -> { 8 System.out.println(num); 9 }; 10 11 //num = num + 2; 12 consumer.accept("hello"); 13 } 14 }
不同点:表达式lambda可以转换为类型Expression<T>的表达式树,而语句lambda不可以
Expression<Func<int, int, int>> expression = (a, b) => a + b;//正确
Expression<Func<int, int, int>> expression1 = (a, b) => { return a + b; };//错误,无法将具有语句体的 lambda 表达式转换为表达式树
1 public interface NoReturnNoParam { 2 3 void method(); 4 5 6 7 default void defaultMethod() { 8 9 System.out.println("this default method "); 10 } 11 }
lambda表达式在只有一条代码时还可以引用其他方法或构造器并自动调用,可以省略参数传递,代码更加简洁,引用方法的语法需要使用::符号。lambda表达式提供了四种引用方法和构造器的方式:
1、引用对象的方法 类::实例方法
2、引用类方法(静态方法) 类::类方法
3、引用特定对象的方法 特定对象::实例方法
4、引用类的构造器 类::new
浙公网安备 33010602011771号