java8新特性lambda表达式

163 阅读8分钟

Lambda表达式

简介

Lambda表达式可以取代大部分匿名内部类,可以优化代码结构。

可以取代匿名内部类?什么意思呢?

在以前如果我们需要对集合排序,我们是这样做:

1Integer[] arr= {3,2,1};
2Arrays.sort(arr, new Comparator<Integer>() {
3
4    @Override
5    public int compare(Integer o1, Integer o2
{
6        return o1-o2;
7    }
8});
9System.out.println(Arrays.toString(arr));
使用Arrays类提供的sort方法传入一个指定排序规则的Comparator,如果我们使用匿名内部类的话,可以看到整个内部类中只用return o1-o2;语句是有用的,其他的都是多余的,为了这一句话我们多写了很多代码。那么,有了Lambda表达式后我们就可以很轻松的解决这个问题了。

java8中新增了Lambda表达式,现在我们可以这样做:

1Integer[] arr= {3,2,1};
2Arrays.sort(arr, (x,y)->x-y);
3System.out.println(Arrays.toString(arr));
那么Lambda是如何实现的呢?我们知道sort方法需要传入一个Comparator,而Comparator是一个接口,那么我们来看看Comparator接口是怎样定义的:
1@FunctionalInterface
2public interface Comparator<T{
Comparator能够支持Lambda表达式的秘密就是类上标注的@FunctionalInterface注解,被这个注解标注的接口只能有一个抽象方法,我们知道我们写Lambda表达式时并没有指定方法,那么当使用Lambda表达式时我们重新的就是这个方法。

Lambda表达式基本语法

语法形式为 () -> {},其中 () 用来描述参数列表,{} 用来描述方法体,-> 为 lambda运算符

  1. 接口无参无返回
 1@FunctionalInterface
2interface NoParamNoReturn {
3    void lambda();
4}
5
6@Test
7public void test() {
8  NoParamNoReturn noParamNoReturn=()->{System.out.println("No param No return");};
9  noParamNoReturn.lambda();
10}
11//如果方法内只有一个语句那么{}可以省略
12@Test
13public void test() {
14  NoParamNoReturn noParamNoReturn=()->System.out.println("No param No return");
15  noParamNoReturn.lambda();
16}
2. 接口有一个或多个参数无返回
 1@FunctionalInterface
2interface OneParamNoReturn{
3    void lambda(int x);
4}
5@Test
6public void test() {
7  OneParamNoReturn oneParamNoReturn=(int x)->System.out.println(x);
8  oneParamNoReturn.lambda(10);
9}
10//如果方法只有一个参数那么()可以省略
11//方法参数的类型也可以省略,编译器会根据方法参数类型推断
12@Test
13public void test() {
14  OneParamNoReturn oneParamNoReturn=x->System.out.println(x);
15  oneParamNoReturn.lambda(10);
16}
3. 接口无参数有返回值
 1@FunctionalInterface
2interface NoParamHasReturn{
3    int lambda();
4}
5@Test
6public void test() {
7  NoParamHasReturn noParamHasReturn=()->{return 10;};
8  noParamHasReturn.lambda();
9}
10//当方法只有return语句时,可以省略{}和return
11@Test
12public void test() {
13  NoParamHasReturn noParamHasReturn=()->10;
14  noParamHasReturn.lambda();
15}
4. 接口有一个或多个参数有返回值
1@FunctionalInterface
2interface HasParamHasReturn{
3    int lambda(int x,int y);
4}
5@Test
6public void test() {
7  HasParamHasReturn hasParamHasReturn=(x,y)->x+y;
8  hasParamHasReturn.lambda(1020);
9}

Lambda表达式引用方法

我们可以使用lambda表达式把接口快速指向一个已经实现了的方法

语法:方法归属者::方法名 静态方法的归属者为类名,普通方法归属者为对象

 1@FunctionalInterface
2interface HasParamHasReturn{
3    int lambda(int x,int y);
4}
5public class LambdaTest{
6  public int add(int x,int y) {
7    return x+y;
8  }
9//lambda表达式指向对象方法
10  @Test public void test() {
11    LambdaTest lt=new LambdaTest();
12    HasParamHasReturn hasParamHasReturn=lt::add;
13    hasParamHasReturn.lambda(1020);
14  }
15
16  public static int sub(int x,int y) {
17    return x-y;
18  }
19
20  //lambda表达式引用静态方法
21  @Test
22  public void test12() {
23    HasParamHasReturn hasParamHasReturn=LambdaTest::sub;
24    hasParamHasReturn.lambda(1020);
25  } 
26}
27
28//类名::实例方法 特殊情况,只有当参数列表为一个参数并且这个参数是方法调用者或多个参数并且第一个参数是调用者其他参数是参数列表
29@Test
30public void test() {
31  BiPredicate<String,String> bp=(x,y)->x.equals(y);
32  //也可以简写为
33  BiPredicate<String,String> bp2=String::equals;
34}
lambda表达式引用构造函数创建对象

语法:类名::new;

 1class User{
2    String name;
3    int age;
4
5    public User() {}
6
7    public User(String name, int age) {
8        super();
9        this.name = name;
10        this.age = age;
11    }
12
13}
14
15@FunctionalInterface
16interface UserCreatorBlankConstruct{
17    User getUser();
18}
19
20//使用lambda表达式引用构造器
21@Test
22public void test() {
23  UserCreatorBlankConstruct creator1=User::new;
24  creator1.getUser();
25}
26
27@FunctionalInterface
28interface UserCreatorParamConstruct{
29    User getUser(String name,int age);
30}
31
32@Test
33public void test13() {
34  UserCreatorParamConstruct creator2=User::new;
35  creator2.getUser("tom"20);
36}

java8为我们提供了4个核心的函数式接口

  1. 消费型接口
1@FunctionalInterface
2public interface Consumer<T{
3void accept(T t);
4}
  1. 供给型接口
1@FunctionalInterface
2public interface Supplier<T{
3    get();
4}
  1. 函数型接口
1@FunctionalInterface
2public interface Function<TR{
3    apply(T t);
4}
  1. 断言型接口
1@FunctionalInterface
2public interface Predicate<T{
3    boolean test(T t);
4}

怎么用呢?

遍历数组

1@Test
2    public void test() {
3        List<Integer> list=new ArrayList<>();
4        list.add(1);
5        list.add(3);
6        list.add(5);
7        list.add(7);
8        list.forEach(System.out::println);
9    }

创建对象

1@Test
2public void test() {
3  Supplier<User> supplier=User::new;
4  User user = supplier.get();
5}

去除前后空格并转为大写

1@Test
2public void test16() {
3  Function<String, String> fun=s->{s=s.trim();s=s.toUpperCase();return s;};
4  String apply = fun.apply(" abCd");
5}

删除集合元素

1@Test
2public void test() 
{
3  List<User> list=new ArrayList<>();
4  list.add(new User("tom",20));
5  list.add(new User("jack",18));
6  list.add(new User("marry",22));
7
8  list.removeIf(e->e.getName()=="jack");
9}

那如果我们要用内置函数式接口创建对象,怎么做呢?

1@Test
2public void test() {
3  Supplier<User> supplier=User::new;
4  User user = supplier.get();
5  System.out.println(user);//User [name=null, age=0]
6}

那到底使用的是哪个构造器呢?通过输出创建的对象可以发现调用的是无参构造器,即调用构造器参数个数对应Supplier中get方法的参数个数的构造器

那么问题又来了,如果我们要使用两个参数的构造器,那Supplier也不行啊,Function的apply方法也只有一个参数,怎么办?那我们去java.util.function包下找找有没有可以用的接口

 1@FunctionalInterface
2public interface BiFunction<TUR{
3    apply(T t, U u);
4}
5//使用内置函数式接口创建对象
6@Test
7public void test20() {
8  BiFunction<String,Integer,User> biFunction=User::new;
9  User user = biFunction.apply("tom"20);
10  System.out.println(user);//User [name=tom, age=20]
11}

四个基本接口参数个数不够用也可以类似的去java.util.function包下找找有没有申明好的函数式接口

最后一个问题,我发现

1list.forEach(System.out::println);

遍历List时,使用的forEach的参数Consumer的accept方法是这么写的,我第一个想到的是,out是System的一个内部类,但是当我点进去,发现是这样的

1public final static PrintStream out = null;

out是一个静态的成员变量,那我的理解就是System.out其实是一个对象

这样System.out::println的写法其实也就是 对象名::方法名

shimo.im/docs/gGQHg8… 《2020年最新Java架构师系统进阶资料免费领取》,可复制链接后用石墨文档 App 或小程序打开