java:lambda 表达式,方法引用

·  阅读 707

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第10天,点击查看活动详情

lambda

什么是 lambda 表达式

  • lambda 表达式本质就是作为只有一个方法的接口的实例。也被称函数式接口。
  • 我们在给一个函数式接口创建实例的时候会使用 lambda 表达式

lambda 格式

lambda格式:(形参 参数名)-> {方法体}

接下来我们看一下格式的对比

  • 传统格式

        Consumer<String> con = new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        };
        con.accept("Consumer");
    复制代码
  • lambda 格式

        Consumer<String> con1 = (String s) -> {
            System.out.println(s);
        };
        con1.accept("lambda");
    复制代码

    (String s): lambda 形参列表。

    ->:lambda 关键字

    {System.out.println(s);}; :lambda 方法体。

我们可以看到使用 lambda 格式要比传统格式写的东西更少更简洁。

只需要方法的形参和方法体即可。


lambda 语法

语法一:无参返回值

@Test
public void test1() {
    Runnable r1 = new Runnable() {
        @Override
        public void run() {
            System.out.println("r1启动");
        }
    };
    r1.run();

    System.out.println("------------- lambda -----------------");

    Runnable r2 = () -> System.out.println("r2启动");
    r2.run();
}
复制代码

如果没有参数的话,lambda 参数列表可以为空。

语法二:有参无返回值

@Test
public void test2() {
    Consumer<String> con = new Consumer<String>() {
        @Override
        public void accept(String s) {
            System.out.println(s);
        }
    };
    con.accept("Consumer");

    System.out.println("------------- lambda -----------------");

    Consumer<String> con1 = (String s) -> {
        System.out.println(s);
    };
    con1.accept("lambda");
}
复制代码

如果方法体的类型是 void,lambda 方法体可以不用写 return 关键字。

语法三:数据类型省略 类型推断

@Test
public void test3() {
    Consumer<String> con1 = (s) -> {
        System.out.println(s);
    };
    con1.accept("类型推断");
}
复制代码

如果接口类是泛型的话,可以不写 lambda 参数可以不写属性,只用写括号和参数名即可。

语法四: Lambda 只有一个参数,参数的小括号可以省略

@Test
public void test4() {
    Consumer<String> con1 = s -> {
        System.out.println(s);
    };
    con1.accept("类型推断");
}
复制代码

如果接口类是泛型且参数只有一个,可以不写 lambda 参数属性和 ‘( )’,只用写参数名即可了

语法五:lambda 需要两个或以上的参数,多条执行语句,并且可以有返回值

@Test
public void test5() {
    Comparator<Integer> com1 = new Comparator<Integer>() {
        @Override
        public int compare(Integer o1, Integer o2) {
            System.out.println(o1);
            System.out.println(o2);
            return o1.compareTo(o2);
        }
    };
    System.out.println(com1.compare(2, 1));

    System.out.println("------------- lambda -----------------");

    Comparator<Integer> com2 = (o1, o2) -> {
        System.out.println(o1);
        System.out.println(o2);
        return o1.compareTo(o2);
    };

    System.out.println(com2.compare(3, 2));
}
复制代码

如果有多个参数,只需要在 lambda 参数列表里写上对应的参数数量即可。

语法六:当 lambda 体只有一条执行语句, return 和大括号可以省略。

@Test
public void test6() {

    Comparator<Integer> com2 = (o1, o2) -> {
        return o1.compareTo(o2);
    };
    System.out.println(com2.compare(3, 2));

    System.out.println("--------------------------------");

    Comparator<Integer> com3 = (o1, o2) -> o1.compareTo(o2);
    System.out.println(com3.compare(3, 2));
}
复制代码

【总结】

  • ->: lambda 关键字
  • (): lambda 形参列表。其中的类型可以省略;如果只有一个参数可以将 () 也省略
  • {}: lambda 方法体。如果只有一条执行语句,return 关键字和 {} 也可以省略。

自定义接口

我们可以创建接口类并且使用 lambda 来创建接口的实例

  1. 首先我们来创建接口

    package com.hyz.lambda.lambdaTest;
    
    /**
     * @author workplace
     * @date 2022/4/23 15:48
     */
    @FunctionalInterface
    public interface LambdaInterface {
        void l1(String a, int b);
    }
    复制代码
  2. 通过 lambda 来创建接口实例

    @Test
    public void test7() {
        LambdaInterface lambdaInterface =
                (String a, int b) -> {
                    System.out.println(a);
                    System.out.println(b);
                };
    
        lambdaInterface.l1("1", 2);
    }
    复制代码

【注意】

  • 我们创建的接口必须是只有一个方法的接口。
  • 在创建接口的时候尽量加上 @FunctionalInterface 注解。这样子就可以自动帮你检测你的接口是否正确。

Java 内置的函数式接口

image-20220423231143529

接下来我们来写一下例子

Consumer<T> 消费型接口

@Test
public void test1() {
    Consumer<String> consumer = s -> {
        s = s + s;
        System.out.println(s);
    };
    consumer.accept("这个字符串会被重复一遍;");
}
复制代码

image-20220424162831225

通过函数式接口可以修改 lambda 方法体对指定类型对象进行操作

Supplier<T> 供给型接口

    @Test
    public void test2() {
        Supplier<String> supplier = () -> "字符串对象被获取";
        System.out.println(supplier.get());
    }
复制代码

可以获取自定义的类。

Function<T, R> 函数型接口

@Test
public void test3() {
    Function<String, Integer> function = a -> a.length();
    System.out.println(function.apply("这个字符串会被重复一遍;"));
}
复制代码

Predicate<T> 判定型接口

@Test
public void test4() {
    Predicate<String> predicate = a -> a.contains("关键字");
    System.out.println(predicate.test("是否存有关键字"));
}
复制代码

方法引用

什么是方法引用

  • 方法引用本质上就是 lambda 表达式。所以方法引用是函数接口的实例。
  • 当 lambda 方法体内的操作已有实现的方法,可以使用方法引用。

方法引用使用格式

方法引用是针对 lambda 表达式的改变。所以本质上使用原来的 lambda 表达式也可以的。

@Test
public void test1() {
    Consumer<String> con1 = s -> System.out.println(s);
    con1.accept("你好程序员");

    System.out.println("----------------------方法引用--------------------------");

    Consumer<String> con2 = System.out::println;
    con2.accept("helloProgrammer");
}
复制代码

【格式】:类( 或对象 ) :: 方法名

具体分为三种情况

  • 情况一: 对象 :: 非静态方法
  • 情况二: 类 :: 静态方法
  • 情况三: 类 :: 非静态方法

情况一和情况二:要求【函数式接口的抽象方法的形参列表和返回值】与【方法引用的形参列表和返回值】相同。

情况三

可以理解为,当 lambda 表达式的方法体内的操作是能够通过某些类的方法实现的话,可以使用方法引用。


对象 :: 方法

    class  People{
        private String name;

        public People(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }
    }

    @Test
    public void test2() {
        People people = new People("hyz");
        Supplier<String> sup1 = () -> people.getName();
        System.out.println(sup1.get());

        System.out.println("----------------------方法引用--------------------------");

        Supplier<String> sup2 = people::getName;
        System.out.println(sup2.get());
    }
复制代码

【分析】

image-20220424174258152

image-20220424175727447

  1. 本来 lambda 方法区内存在一个 getName 的方法。
  2. 但是这个方法已经在 People 类中被实现了(已经被完全写好,只需要传参调用或直接调用)。
  3. People 方法的参数和返回值和函数式接口的参数和返回值一致,所以可以使用 方法引用。
  4. 我们可以通过 对象名 :: 方法名 来代替 lambda 表达式。

类 :: 静态方法

@Test
public void test3() {
    Comparator<Integer> con1 = (t1, t2) -> Integer.compare(t1, t2);
    System.out.println(con1.compare(2, 3));

    System.out.println("----------------------方法引用--------------------------");

    Comparator<Integer> con2 = Integer::compare;
    System.out.println(con2.compare(2,3));
}
复制代码

【分析】

  1. lambda 方法体内的方法已经被实现
  2. 被实现的方法的参数和返回值与函数式接口的参数和返回值相同
  3. 被实现的方法是静态方法,所以选择通过 【类名 :: 静态方法】 来引用。

类 :: 非静态方法

我们先创建一个 Book 类在来测试。

    class Book{
        private String name;

        public Book(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }
    }

    @Test
    public void test4() {
        Book book = new Book("hyz");

        Function<Book,String> fun1=new Function<Book, String>() {
            @Override
            public String apply(Book book) {
                return book.getName();
            }
        };
        System.out.println(fun1.apply(book));

        System.out.println("----------------------lambda--------------------------");


        Function<Book, String> fun2 = b -> b.getName();
        System.out.println(fun2.apply(book));

        System.out.println("----------------------方法引用--------------------------");

        Function<Book, String> fun3 = Book::getName;
        System.out.println(fun3.apply(book));
    }
复制代码

【分析】

类::非静态方法

  1. lambda 方法体已被 Book 类实现
  2. 被实现的方法的参数和返回值与函数式接口的参数和返回值不相同,但是两个参数之间的关系是相互调用
  3. 被实现的方法是非静态方法,所以选择通过 【类名 :: 非静态方法】 来引用。

方法引用的规律

  1. 先看 lambda 方法区的方法是否已经被实现
  2. 再看被实现的方法和对应的函数式接口的方法是否匹配
  3. 如果不会写就写 lambda 格式,实在不行就用匿名方法。
分类:
后端
标签:
收藏成功!
已添加到「」, 点击更改