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 表达式转换为表达式树
  default :在接口中定义非抽象方法 ,switch-case-default 中也有用到
 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

 
posted @ 2019-11-05 19:09  Classis_Man  阅读(247)  评论(0)    收藏  举报