Lambda表达式和方法的引用

Java知识点总结:想看的可以从这里进入

13.1、Lambda表达式

13.1.1、简介

Lambda 表达式(Lambda expression)是一个匿名函数,基于数学中的λ演算得名,也可称为闭包(Closure)。java、Python、JavaScript等等都支持 Lambda 表达式。

它是Java8的一个重要特性,我们可以把Lambda表达式当成是为是一段可以传递的代码,使用它能使代码更加简洁。

(参数列表) -> { // Lambda表达式体 
}
->Lambda 操作符
->的左侧代码:Lambda 表达式所需的参数列表。(一个参数无需定义圆括号,但多个参数需要定义圆括号)
->的右侧代码:Lambda 表达式中所需执行的功能,用{ }包起来,即 Lambda 体。(如果只包含了一个语句,可以不写大括号。)

这里有一个注解:@FunctionalInterface,通过 @FunctionalInterface 标记的接口可以通过 Lambda 表达式创建实例(也就是函数式接口)

image-20230219195533600

比如我们用过的Runnable 接口,它就 有@FunctionalInterface 注解

image-20230219195723694
13.1.2、简单使用

我们可以使用Runnable来测试一下使用Lambda的效果

  • 不使用

    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("没有使用lambda");
            }
        }).start();
    }
    
  • 使用Lambda表达式

    public static void main(String[] args) {
        new Thread(() -> {
           System.out.println("使用了lambda");
       }).start();
       //还可以进一步省略
       new Thread(() -> System.out.println("使用了lambda")).start();
    }
    

可以明显看到使用 lambda表达式后,代码明显简洁了很多。

lambda表达式的几种写法:

  1. ()中不需要声明参数的类型,JVM会进行自动识别。

  2. 如果仅有一个参数时圆括号可以不写,但是无参数或多个参数需要定义圆括号。

  3. 如果Lambda表达式体 只有一个语句可以直接省略大括号

  4. 如果主体中仅仅只有一个返回语句,则大括号和关键字return都可以省略。

    //无参,无返回值
    Runnable runnable2 = () -> {
        System.out.println("无参数无返回值");
    };
    Runnable runnable1 = () -> System.out.println("无参数无返回值可省略{");
    
    //一个参数,无返回值
    Consumer<String> c1 = (String s) -> {
        System.out.println("格式写全");
    };
    Consumer<String> c2 = (s) -> {
        System.out.println("可以省略数据类型");
    };
    Consumer<String> c3 = s -> System.out.println("数据类型、()和{}都可以省略");
    
    //两个以上参数,多条执行语句,返回值
    Comparator<Integer> comparator = (x, y) -> {
        System.out.println("多参数,()不能省略,数据类型可省略");
        System.out.println("多条句,{}不能省略");
        return Integer.compare(x,y);
    };
    
    //只有一个返回语句
    Function<String,String> f1 = num -> "只有一个返回语句return和{}都能省略";
    
13.1.3、函数式接口

Lambda 表达式可以代替匿名内部类使用,使代码更加简洁。但不是所有的匿名内部类都可以简化为Lambda表达式,只有声明为函数式接口的匿名内部类才可以使用Lambda表达式来进行简化。

函数式接口:即接口中只有一个抽象的方法。如果接口内实现了多个方法,使用 Lambda 表达式会出错。可以使用注解@FunctionalInterface声明函数式接口,接口只要添加了该注解,就不可以再增加新的方法了。

//声明一个函数式的接口
@FunctionalInterface
public interface FunctionalInterfaceTest {
    String showMessage(String name,String message);
}

public static void main(String[] args) {
    //使用匿名内部类
    FunctionalInterfaceTest functionalInterface1 = new FunctionalInterfaceTest() {
        @Override
        public String showMessage(String name, String message) {
            System.out.println(name+"传达消息:"+message);
            return "传达成功";
        }
    };
    String s1 = functionalInterface1.showMessage("匿名内部类", "实现函数式接口");
    System.out.println(s1);

    //Lambda 表达式代替匿名内部类
     FunctionalInterfaceTest functionalInterface2 =(name,message) -> {
        System.out.println(name+"传达消息:"+message);
        return "传达成功";
    };
    String s2 = functionalInterface2.showMessage("Lambda", "实现函数式接口");
    System.out.println(s2);
}

image-20220909112656851

虽然 Lambda使代码简洁,方便函数式编程,容易进行并行计算。但是它也容易导致代码的可读性变差。

13.1.4、当成参数传递

如果我们在方法中传递的参数是函数式接口的,那么我们可以直接使用 Lambda 表达式作为参数传递进去

public static void main(String[] args) {
    test((name,message) -> {
        System.out.println(name+"传达消息:"+message);
        return "";
    },"aaa","bbb");

}
public static void test(FunctionalInterfaceTest interfaceTest,String name ,String message){
    interfaceTest.showMessage(name,message);
}

image-20220909115551188

13.1.5、内置函数接口

在JDK中内置了四大核心函数式接口

函数式接口参数类型返回类型方法用途
Consumer消费型接口Tvoidvoid accept(T t)只能接受一个输入参数并且无返回值
Supplier 供给型接口TT get();返回类型为T的对象
Function<T, R>函数型接口TRR apply(T t)对类型为T的对象应用操作,并返回结果。结果是R类型的对象
Predicate断言型接口Tbooleanboolean test(T t)确定类型为T的对象是否满足某约束,并返回boolean
@Test
public void mainTest(){
    //消费型接口。无返回值,有参数
    Consumer<String> consumer = (name)->{
        System.out.println("===测试consumer===");
        System.out.println(name);
    };
    consumer.accept("张三");

    //供给型接口,无参数有返回值
    Supplier<String> supplier = () -> {
        System.out.println("===测试supplier===");
        return "supplier的返回值";
    };
    String s = supplier.get();
    System.out.println(s);

    //函数型接口,有参数有返回值
    Function<String, String> function = (name)-> {
        System.out.println("===测试function===");
        return "返回值是:"+name;
    };
    String apply = function.apply("张三");
    System.out.println(apply);

    //断言型接口
    Predicate<String> predicate = (name) ->{
        System.out.println("===测试predicate===");
        return "张三".equals(name);
    };
    boolean b = predicate.test("张三");
    System.out.println(b);

}

image-20230220144005550

DK 1.8 之前已有的函数式接口:

  • java.lang.Runnable
  • java.util.concurrent.Callable
  • java.security.PrivilegedAction
  • java.util.Comparator
  • java.io.FileFilter
  • java.nio.file.PathMatcher
  • java.lang.reflect.InvocationHandler
  • java.beans.PropertyChangeListener
  • java.awt.event.ActionListener
  • javax.swing.event.ChangeListener

JDK 1.8 新增加的函数接口:

  • java.util.function:它内部包含了众多的接口

    • 序号接口 & 描述
      1四大内置接口
      2**BiConsumer<T,U>**代表了一个接受两个输入参数的操作,并且不返回任何结果
      3**BiFunction<T,U,R>**代表了一个接受两个输入参数的方法,并且返回一个结果
      4**BinaryOperator**代表了一个作用于于两个同类型操作符的操作,并且返回了操作符同类型的结果
      5BiPredicate<T,U> 代表了一个两个参数的boolean值方法
      6BooleanSupplier代表了boolean值结果的提供方
      7DoubleBinaryOperator代表了作用于两个double值操作符的操作,并且返回了一个double值的结果。
      8DoubleConsumer代表一个接受double值参数的操作,并且不返回结果。
      9**DoubleFunction**代表接受一个double值参数的方法,并且返回结果
      10DoublePredicate代表一个拥有double值参数的boolean值方法
      11DoubleSupplier代表一个double值结构的提供方
      12DoubleToIntFunction接受一个double类型输入,返回一个int类型结果。
      13DoubleToLongFunction接受一个double类型输入,返回一个long类型结果
      14DoubleUnaryOperator接受一个参数同为类型double,返回值类型也为double 。
      15IntBinaryOperator接受两个参数同为类型int,返回值类型也为int 。
      16IntConsumer接受一个int类型的输入参数,无返回值 。
      17**IntFunction**接受一个int类型输入参数,返回一个结果 。
      18IntPredicate:接受一个int输入参数,返回一个布尔值的结果。
      19IntSupplier无参数,返回一个int类型结果。
      20IntToDoubleFunction接受一个int类型输入,返回一个double类型结果 。
      21IntToLongFunction接受一个int类型输入,返回一个long类型结果。
      22IntUnaryOperator接受一个参数同为类型int,返回值类型也为int 。
      23LongBinaryOperator接受两个参数同为类型long,返回值类型也为long。
      24LongConsumer接受一个long类型的输入参数,无返回值。
      25**LongFunction**接受一个long类型输入参数,返回一个结果。
      26LongPredicateR接受一个long输入参数,返回一个布尔值类型结果。
      27LongSupplier无参数,返回一个结果long类型的值。
      28LongToDoubleFunction接受一个long类型输入,返回一个double类型结果。
      29LongToIntFunction接受一个long类型输入,返回一个int类型结果。
      30LongUnaryOperator接受一个参数同为类型long,返回值类型也为long。
      31**ObjDoubleConsumer**接受一个object类型和一个double类型的输入参数,无返回值。
      32**ObjIntConsumer**接受一个object类型和一个int类型的输入参数,无返回值。
      33**ObjLongConsumer**接受一个object类型和一个long类型的输入参数,无返回值。
      34**ToDoubleBiFunction<T,U>**接受两个输入参数,返回一个double类型结果
      35**ToDoubleFunction**接受一个输入参数,返回一个double类型结果
      36**ToIntBiFunction<T,U>**接受两个输入参数,返回一个int类型结果。
      37**ToIntFunction**接受一个输入参数,返回一个int类型结果。
      38**ToLongBiFunction<T,U>**接受两个输入参数,返回一个long类型结果。
      39**ToLongFunction**接受一个输入参数,返回一个long类型结果。
      40**UnaryOperator**接受一个参数为类型T,返回值类型也为T。

13.2、方法的引用

方法引用通过方法的名字来指向一个方法,它的符号是 :: 两个冒号,一般配合Lambda表示一起使用,使用代码更加简洁。

1、方法引用

当要传递给Lambda体的操作,在lambad体中的内容有方法已经实现了,这时候就可以使用方法引用(实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值类型保持一致!),它有三种格式:

  1. 对象 :: 实例方法名称

    //消费型接口。不使用方法引用
    Consumer<String> consumer = (name)->{
        System.out.println(name);
    };
    consumer.accept("张三");
    
    //使用方法引用
    Consumer<String> consumer1 = System.out::println;
    consumer.accept("张三");
    

    image-20230220142603225

  2. 类名称 :: 静态方法名称

    Comparator<Integer> com1 = (x, y)->Integer.compare(x, y);
    System.out.println(com1.compare(2,2));
    
    //使用方法引用
    Comparator<Integer> com2 = Integer::compare;
    System.out.println(com2.compare(2,2));
    

    image-20230220144340077

    image-20230220144323358

  3. 类名称 :: 实例方法名称

    //未使用方法引用
    BiPredicate<String,String> bp1 = (x, y)->x.equals(y);
    System.out.println(bp1.test("张三","里斯"));
    
    //使用方法引用
    BiPredicate<String,String> bp2 = String::equals;
    System.out.println(bp2.test("张三","张三"));
    

    image-20230220144552953

在 jdk1.8 后 ,新增了很多可以用lambda表达式代替的方法,比如:Iterable中增加 forEach方法、Arrays操作数组中的排序方法void sort(T[] a, Comparator<? super T> c)等等,都可以把 Lambda 表达式当成参数进行传递。

default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
image-20220908190713606
2、构造器引用

属于方法引用的一种变体,格式:ClassName::new 或者 ClassName<>::new

image-20230220150838945
Supplier<Persion> s1 = () -> new Persion("张三");
System.out.println(s1.get().getName());

Supplier<Persion> s2 = Persion::new;
System.out.println(s2.get().getName());

image-20230220151459858

3、数组引用

格式:type[] :: new

Function<Integer,Integer[]> f1 = (n) -> new Integer[n];
Integer[] apply1 = f1.apply(5);
System.out.println(Arrays.toString(apply1));
System.out.println(apply1.length);

Function<Integer,Integer[]> f2= Integer[]::new;
Integer[] apply2 = f2.apply(5);
System.out.println(Arrays.toString(apply2));
System.out.println(apply2.length);

image-20230220152508575

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

辰 羽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值