函数式编程:Lambda 表达式

news/2024/5/5 15:04:42/文章来源:https://blog.csdn.net/weixin_61635597/article/details/129365317

函数式编程:Lambda 表达式

在这里插入图片描述

每博一文案

曾经读过的依然令我感动的句子,生活总是不如意,但往往是在无数痛苦中,但往往是在无数痛苦中,在重重矛盾

和艰难中才能成熟起来,坚强起来,爱情啊,它使荒芜变成繁荣,平庸变得伟大,使死去的复活,活着的闪闪发光,

即使爱情是不尽的煎熬折磨,像冰霜般严厉,烈火般烤灼,但爱情对心和身体健男女是那样的自然,同时又永远让我们感到新奇神秘和不可思议… 。生活中真正的勇士向来默默无闻,喧哗不止的永远是自视高贵的一群,无论精神是多么独立,感情却总是在寻找以一种依附,寻找一种归宿,亲人之间的感情是多么重要,假如没有这种感情,我们活在这个世界是多么悲哀啊,只有劳动才能使人在

生活中强大,不论什么人,最终还是要崇尚哪些用双手创造生活的财富者,人们宁愿关心一个蹩脚的电影演。

​ —————— 《平凡的世界》路遥

文章目录

  • 函数式编程:Lambda 表达式
    • 每博一文案
    • 1. 函数式编程
    • 2. Lambda 表达式概述
    • 3. Lambda 表达式的六种语法格式
      • 3.1 第一种:无参,无返回值,一条执行语句
      • 3.2 第二种:有参数,无返回值,一条语句
      • 3.3 第三种:有参数,参数的数据类型可以省略,类型推断
      • 3.4 第四种:只有一个参数,无返回值,一条语句
      • 3.5 第五种:有多个参数,多条语句,有返回值
      • 3.6 第六种:有多个参数,有返回值,一条语句
      • 3.7 Lambda 表达式六种语法格式总结:
      • 3.8 补充:作为参数传递 Lambda 表达式
    • 4. 函数式接口的概述
      • 4.1 自定义函数式接口
    • 5. Java 内置四大核心函数式接口
      • 5.1 Consumer
      • 5.2 Supplier
      • 5.3 Function
      • 5.4 Predicate
      • 5.5 其他的函数式接口
    • 6. 方法引用
      • 6.1 情况1:对象::实例方法名
      • 6.2 情况2:类::静态方法
      • 6.3 情况3:类::实例方法
    • 7. 构造器引用
    • 8. 数组引用
    • 9. Lambda 表达式的优缺点:
    • 10. 总结:
    • 11. 最后:

1. 函数式编程

我们先看看什么是函数。函数是一种最基本的任务,一个大型程序就是一个顶层函数调用若干底层函数,这些被调用的函数又可以调用其他函数,即大任务被一层层拆解并执行。所以函数就是面向过程的程序设计的基本单元。

Java不支持单独定义函数,但可以把静态方法视为独立的函数,把实例方法视为自带this参数的函数。

而函数式编程(请注意多了一个“式”字)——Functional Programming,虽然也可以归结到面向过程的程序设计,但其思想更接近数学计算。

我们首先要搞明白计算机(Computer)和计算(Compute)的概念。

在计算机的层次上,CPU执行的是加减乘除的指令代码,以及各种条件判断和跳转指令,所以,汇编语言是最贴近计算机的语言。

而计算则指数学意义上的计算,越是抽象的计算,离计算机硬件越远。

对应到编程语言,就是越低级的语言,越贴近计算机,抽象程度低,执行效率高,比如C语言;越高级的语言,越贴近计算,抽象程度高,执行效率低,比如Lisp语言。

函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。

函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数!

函数式编程最早是数学家阿隆佐·邱奇研究的一套函数变换逻辑,又称Lambda Calculus(λ-Calculus),所以也经常把函数式编程称为Lambda计算。

Java平台从Java 8开始,支持函数式编程。

2. Lambda 表达式概述

相信大家一定在相关的框架的源码中看到不少 使用了 lambda 表达式的内容吧。如果我们想要阅读框架的源码上的逻辑,则必须了解,明白 Lambda 表达式的格式,语法了。

Java Lambda 表达式是 Java8 的新特性。Java lambda 表达式是 Java进入函数式编程的第一步。因此,Java lambda 表达式时可以单独创建的,而无需属于任何类。这一点很重要。Java Lambda 表达式可以像对象一样传递并按需执行。

Java lambda 表达式通常用于实现 简单的事件监听/回调,或在 Java Streams API 函数式编程时使用。

Lambda 是一个匿名函数 ,我们可以把 Lambda 表达式理解为是 一段可以传递的代码 (将代码像数据一样进行传递)。使用它可以写出更简洁,更灵活的代码。作为一种更紧凑的代码风格,使Java 语言表达能力得到了提升。

Lambda 表达式的本质:就是作为接口的实例。简单的说就是对 匿名实现接口的替换。 因为Java当中的接口是 不能 new 的,想要 new 的用该接口的话,就只能 new 该接口的实现类了。或者匿名实现接口。记住这个概念,只要你理解了这句话,那 Lambda 就十分简单了。

如下简单的举例:

如下是不使用 Lambda 表达式的方式,而是简单的匿名实现接口的方式。处理的

package blogs.blog13;public class LambdaTest01 {public static void main(String[] args) {Runnable run = new Runnable() {@Overridepublic void run() {System.out.println("你好世界");}};run.run();}
}

在这里插入图片描述

同样的结果,使用 Lambda 表达式处理。

package blogs.blog13;public class LambdaTest01 {public static void main(String[] args) {Runnable runnable = ()->System.out.println("你好世界");runnable.run();}
}

在这里插入图片描述

从上述两个使用同样的功能,但是 使用 Lambda 表达式解决的代码量更少一些。

3. Lambda 表达式的六种语法格式

Lambda 表达式:在 Java 8 语言中引入的一种新的语法元素和操作符。这个操作符为 -> 一杠 + 一个左箭头,该操作符被称为 Lambda 操作符箭头操作符 。它将 Lamdba 分为两个部分。

箭头左侧 : 指定了 Lambda 表达式需要的参数列表 。其实就是对应接口中的重写抽象方法中的参数列表。

箭头右侧 :指定了 Lambda 体,即为 Lambda 表达式要执行的功能。其实就是对应接口中共重写抽象方法中的所要执行的语句/处理的逻辑。

一般的具体格式如下:

Runnable runnable = ()->System.out.println("你好世界");()-> { lambda 体所要执行的语句}

我们知道 Lambda 表达式的本质就是:接口的实例化。匿名实现接口的替换 。既然要实现 接口 ,自然也就要重写其接口的抽象方法了。不同的接口中的抽象方法其结构也是不一样的。既然抽象方法都不一样了,那对应的接口中的 Lambda 表达式也有所不同了。

具体可以分为如下六种对应不同接口中的抽象方法中的不同的 Lambda 表达式的语法格式

这里我们会通过实现同样的功能,使用 匿名实现接口Lambda 表达式 进行处理,两者之间进行一个对比,这样更容易理解 Lambda 表达式。

3.1 第一种:无参,无返回值,一条执行语句

情况1: 接口中的只有一个抽象方法,该抽象方法中:无参数,无返回值,只有一条语句 。

Lambda 表达式的语法格式如下:

() -> 要执行的语句 // 一条语句 {} 可以省略

补充: Runable 接口的源码:

在这里插入图片描述

匿名实现接口的方式:

package blogs.blog13;public class LambdaTest01 {public static void main(String[] args) {Runnable run = new Runnable() {@Overridepublic void run() {System.out.println("你好世界");}};run.run();}
}

在这里插入图片描述

Lambda 表达式的方式:

package blogs.blog13;public class LambdaTest01 {public static void main(String[] args) {Runnable runnable = ()->System.out.println("你好世界");// () 参数列表:重写接口中抽象方法的//System.out.println("你好世界"): 重写接口中抽象方法的中执行的语句runnable.run();}
}

在这里插入图片描述

3.2 第二种:有参数,无返回值,一条语句

情况2: 一个接口中只有一个抽象方法,该抽象方法:有参数,无返回值,只有一条语句。

Lambda 表达式的语法格式如下:

Consumer<String> consumer = (String s)-> System.out.println(s);
(参数的数据类型 参数名,参数的数据类型,参数名)-> 执行的语句;

补充: Consumer 接口的源码
在这里插入图片描述

匿名实现接口的方式:


import java.util.function.Consumer;public class LambdaTest01 {public static void main(String[] args) {Consumer<String> consumer = new Consumer<String>() {@Overridepublic void accept(String s) {System.out.println(s);}};consumer.accept("你好");  // 调用接口中重写的抽象方法}
}

在这里插入图片描述

Lambda表达式的方式:

import java.util.function.Consumer;public class LambdaTest01 {public static void main(String[] args) {Consumer<String> consumer = (String s)-> System.out.println(s);consumer.accept("你好");  // 调用接口中重写的抽象方法}
}

在这里插入图片描述

在这里插入图片描述

3.3 第三种:有参数,参数的数据类型可以省略,类型推断

情况3: 接口中的抽象方法有参数,但是该参数的数据类型可以省略不写(你也可以写上),Java自动会自动类型的推断,与 泛型 ,数组中的类型推断类似的。

Lambda 表达式的语法格式如下:

Consumer<String> consumer = (s)-> System.out.println(s); // 数据类型可以省略,该数据类型Java会自行推断出来。
(参数名)-> 执行的语句;

匿名实现接口的方式:

import java.util.function.Consumer;public class LambdaTest01 {public static void main(String[] args) {Consumer<String> consumer = new Consumer<String>() {@Overridepublic void accept(String s) {System.out.println(s);}};consumer.accept("你好");  // 调用接口中重写的抽象方法}
}

在这里插入图片描述

Lambda表达式的方式:

import java.util.function.Consumer;public class LambdaTest01 {public static void main(String[] args) {Consumer<String> consumer = (s)-> System.out.println(s);consumer.accept("你好");  // 调用接口中重写的抽象方法}
}

在这里插入图片描述

3.4 第四种:只有一个参数,无返回值,一条语句

情况4: 接口中的抽象方法:只有一个参数,无返回值,一条语句。

Lambda 表达式的语法格式如下:

Consumer<String> consumer = s-> System.out.println(s); //数据类型可以省略(Java自动推断出来),一个参数 () 圆括号可以省略,一条语句{} 可以省略
参数-> 执行的语句

Lambda表达式的方式:

import java.util.function.Consumer;public class LambdaTest01 {public static void main(String[] args) {Consumer<String> consumer = s-> System.out.println(s);consumer.accept("你好");  // 调用接口中重写的抽象方法}
}

在这里插入图片描述

3.5 第五种:有多个参数,多条语句,有返回值

情况5: 接口中的抽象方法:有多个参数,多条语句,有返回值。

Lambda 表达式的语法格式如下:

Comparator<Integer> comparator = (o1,o2)->{System.out.println(o1);System.out.println(o2);return o1.compareTo(o2);};
/* (参数名,参数名) -> {执行的多条语句;执行的多条语句;return  返回值;
}  参数类型可以省略Java自行推断出来,多条语句使用{} 花括号括起来, return 返回值。
*/// 或者Comparator<Integer> comparator1 = (o1,o2)->{System.out.println(o1);System.out.println(o2);return Integer.compare(o1,o2);};

补充: Comparator 部分源码

@FunctionalInterface
public interface Comparator<T> {int compare(T o1, T o2);
}

匿名实现接口的方式:

import java.util.Comparator;public class LambdaTest01 {public static void main(String[] args) {Comparator<Integer> comparator = new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {System.out.println(o1);System.out.println(o2);return o1.compareTo(o2);//或者 return Integer.compare(o1,o2);}};int compare = comparator.compare(21, 12);// 调用该接口中重写的抽象方法System.out.println(compare);}
}

在这里插入图片描述

Lambda表达式的方式:


import java.util.Comparator;public class LambdaTest01 {public static void main(String[] args) {Comparator<Integer> comparator = (o1,o2)->{System.out.println(o1);System.out.println(o2);return o1.compareTo(o2);};int compare = comparator.compare(21, 12);// 调用该接口中重写的抽象方法System.out.println(compare);System.out.println("*****************************");// 或者Comparator<Integer> comparator1 = (o1,o2)->{System.out.println(o1);System.out.println(o2);return Integer.compare(o1,o2);};int compare2 = comparator.compare(21, 12);// 调用该接口中重写的抽象方法System.out.println(compare2);}
}

在这里插入图片描述

3.6 第六种:有多个参数,有返回值,一条语句

情况6: 接口中抽象方法:有多个参数,有返回值,只有一条语句

Lambda 表达式的语法格式如下:

Comparator<Integer> comparator = (o1,o2)->Integer.compare(o1,o2);
// (参数名1,参数名2)-> return 返回的值
// 当接口中的抽象方法只有一个返回值时,其 {} 和 return 都可以省略,注意:要么两者都省略,要么都不省略,不然编译无法通过的。

匿名实现接口的方式:


import java.util.Comparator;public class LambdaTest01 {public static void main(String[] args) {Comparator<Integer> comparator = new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {System.out.println(o1);System.out.println(o2);return o1.compareTo(o2);//或者 return Integer.compare(o1,o2);}};int compare = comparator.compare(21, 12);// 调用该接口中重写的抽象方法System.out.println(compare);}
}

在这里插入图片描述

Lambda表达式的方式:

import java.util.Comparator;public class LambdaTest01 {public static void main(String[] args) {Comparator<Integer> comparator = (o1,o2)->Integer.compare(o1,o2);int compare = comparator.compare(21, 12);// 调用该接口中重写的抽象方法System.out.println(compare);}
}

在这里插入图片描述

当接口中的抽象方法只有一个返回值时,其 {} 和 return 都可以省略,注意:要么两者都省略,要么都不省略,不然编译无法通过的。

在这里插入图片描述

3.7 Lambda 表达式六种语法格式总结:

  • 在 Lambda 表达式中:接口中重写的抽象方法中的参数的数据类型可以省略,Java会自行推断出来的。
  • 如果 Lambda 表达式中只有一个参数,其一对 {} 花括号可以省略。
  • 如果 Lambda 表达式中只有一条语句一个返回值,则:return 关键字和 {} 花括号都可以省略,注意: :要么两者都省略,要么都不省略,不然编译无法通过的
  • 如果接口中 的抽象方法有多条语句,则需要使用 {} 花括号。括起来。
  • Lambda 表达式的本质:就是函数式接口的实例。替换匿名实现类的方式
  • 如果一个接口中,只声明了一个抽象方法,(被 static ,default 修饰的抽象方法不算)。则此接口就称为 “函数式接口”。

上述Lambda 表达式中的参数类型都是由编译器推断得出的。Lambda 表达式中无需指定类型,程序依然可以编译,这是因为javac根据程序的上下文,在后台推断出了参数的类型。Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的。这就是所谓的 “类型推断”

在这里插入图片描述

3.8 补充:作为参数传递 Lambda 表达式

作为参数传递 Lambda 表达式:为了将 Lambda 表达式作为参数传递,接收Lambda 表达式的参数类型必须是与该 Lambda 表达式兼容的函数式接口的类型。

在这里插入图片描述

举例一:

匿名实现接口的方式:

package blogs.blog13;import java.util.function.Consumer;public class LambdaTest02 {public static void happyTime(double money, Consumer<Double> consumer) {consumer.accept(money);}// 匿名实现接口 传递接口实例public static void main(String[] args) {// 调用方法happyTime(600,new Consumer<Double>() {// 重写 Consumer 接口中的 accept()抽象方法@Overridepublic void accept(Double aDouble) {System.out.println("Hello World");}});}}

在这里插入图片描述

Lambda 表达式处理


import java.util.function.Consumer;public class LambdaTest02 {public static void happyTime(double money, Consumer<Double> consumer) {consumer.accept(money);}// Lambda 表达式处理public static void main(String[] args) {// 调用方法happyTime(600,(Double d)-> System.out.println("Hello World"));// (Double d) 是 Consumer 接口中 accept()抽象方法的参数// System.out.println("Hello World") 是 Consumer 接口中 accept()抽象方法执行的语句。}
}

在这里插入图片描述

举例二:

匿名实现接口的方式:


import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;public class LambdaTest02 {/*** 根据给定的规则,过滤集合中的字符串,此规则由 Predicate 的方法决定*/public static List<String> filterString(List<String> list, Predicate<String> pre) {ArrayList<String> filterList = new ArrayList<>();for(String s : list) {if(pre.test(s)) {filterList.add(s);}}return filterList;}public static void main(String[] args) {List<String> list = Arrays.asList("北京","天津","南京","东京","西京");List<String> list2 = filterString(list, new Predicate<String>() {// 重写 Predicate 接口中的 test(T t) 抽象方法@Overridepublic boolean test(String s) {return s.contains("京"); // 字符串中含有 京 字的返回 true,否则返回 false}});System.out.println(list2);}
]

在这里插入图片描述

Lambda 表达式处理


import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;public class LambdaTest02 {/*** 根据给定的规则,过滤集合中的字符串,此规则由 Predicate 的方法决定*/public static List<String> filterString(List<String> list, Predicate<String> pre) {ArrayList<String> filterList = new ArrayList<>();for(String s : list) {if(pre.test(s)) {filterList.add(s);}}return filterList;}public static void main(String[] args) {List<String> list = Arrays.asList("北京","南京","天津","东京","西京");List<String> list2 = filterString(list, (String s) -> {return s.contains("京");});  // 或者 s->s.contaions("京"); // 数据类型可以省略,一条语句 return {}也可以省略System.out.println(list2);}
}

在这里插入图片描述

4. 函数式接口的概述

  • 只包含一个抽象方法的接口,称之为 函数式接口
  • 你可以通过 Lambda 表达式来创建该接口的对象,(若 Lambda 表达式抛出一个受检异常即:非运行时异常),那么该异常需要在目标接口的抽象方法中进行声明。
  • 我们可以在一个接口上使用 @Functionallnterface 注解,这样就可以检查它是否是一个函数式接口。同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。
  • 如果我们在某个接口上声明了 @FunctionalInterface 注解,那么编译器就会按照函数式接口的定义来要求该接口,这样如果有两个抽象方法,程序编译就会报错的。所以,从某种意义上来说,只要你保证你的接口中只有一个抽象方法,你可以不加这个注解。加上就会自动进行检测的,保证安全。
  • java.util.functio 包下定义了 java8 丰富的函数式接口。

在这里插入图片描述

Java从诞生日起就是一直倡导 “一切皆对象” ,在Java里面**面向对象(OOP)**编程是一切。但是随着 python,scala 等语言的兴起和新技术的挑战,Java 不得不做出调整以便支持更加广泛的技术要求,也即 java 不但可以支持 OOP 还可以支持 OOF(面向函数编程)

  • 在 函数式编程语言当中,函数被当做一等公民对待。在将函数作为一等公民的编程语言中,Lambda 表达式的类型是函数。但是在Java8 中,有所不同。在 Java8 中,Lambda 表达式是对象,而不是函数,它们必须依附于一类特别的对象类型 —— 函数式接口
  • 简单的说,在 Java8 中,Lambda 表达式就是一个函数式接口的实例。这就是 Lambda 表达式和函数式接口的关系。也就是说,只要一个对象时函数式接口的实例,那么该对象就可以用 Lambda 表达式来表示。所以以前用匿名实现类表示的现在都可以用 Lambda 表达式来写了。

4.1 自定义函数式接口

@FunctionalInterface
public interface MyInterface01 {public abstract void fun();
}

函数式接口中使用泛型


@FunctionalInterface
public interface MyInterface01<T> {public abstract T fun(T t);
}

5. Java 内置四大核心函数式接口

5.1 Consumer

函数式接口参数类型返回类型作用
Consumer消费型接口Tvoid对类型为 T 的对象应用操作,包含抽象方法:void accept(T t)

在这里插入图片描述

在这里插入图片描述

5.2 Supplier

函数式接口参数类型返回类型作用
Suppolier 供给型接口T返回类型为 T 的对象,包含抽象方法为:T get()

在这里插入图片描述

在这里插入图片描述

5.3 Function

函数式接口参数类型返回类型作用
Function<T,R> 函数型接口TR对类型为 T 的对象应用操作,并返回结果。结果为 R 类型的对象,包含抽象方法:R apply(T t)

在这里插入图片描述

在这里插入图片描述

5.4 Predicate

函数式接口参数类型返回类型作用
Predicate 断定型接口Tboolean确定类型为 T的对象是否满足某约束,并返回 boolean 值。包含抽象方法:boolean test(T t)

在这里插入图片描述

在这里插入图片描述

5.5 其他的函数式接口

在这里插入图片描述

6. 方法引用

  • 当要传递 给 Lambda表达式中(也就是接口中的抽象方法中),内部直接(只是)调用的是其他类中已经实现的方法,没有其他的处理语句了,就可以使用方法引用
  • 方法引用可以看做是 Lambda 表达式深层次的表达。换句话说:方法引用就是 Lambda表达式,可以理解为是 Lambda 的简写 ,同样的既然 方法引用就是 Lambda 表达式,那也就是 函数式接口的一个实例。因为:Lambda 表达式的本质:就是作为接口的实例。简单的说就是对 匿名实现接口的替换。通过方法的名字来指向一个方法,可以认为是 Lambda 表达式的一个语法糖

想要使用方法引用:需要满足一些条件: 实现的接口的抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值类型保持一致。换句话说:就是我们接口中重写的抽象方法内部调用的其他类已经实现的方法的,这两者之间的方法的(参数列表)和 return 返回类型要一致,不然不可以使用 方法引用。

如下图

在这里插入图片描述

使用 方法引用的语法格式如下: 使用操作符 :: 将类(或对象) 与方法名分隔开来。

import java.util.function.Consumer;public class MethodRefTest {public static void main(String[] args) {Consumer<String> consumer = System.out::println;consumer.accept("Hello World");}
}

常用的方法引用有如下三种情况:

  • 对象::实例方法名 : 对象名引用实例方法
  • 类::静态方法名 :类名引用静态方法
  • 类:: 实例方法名 类名引用实例方法名。这里这里没有写错。
  • 注意对象不可引用静态方法

这里会将上述三种情况:分别使用 Lambda 表达式 与 方法引用,匿名实现接口 处理同一个功能,通过比较这三种方式,来理解方法引用

6.1 情况1:对象::实例方法名

补充: Consumer 和 PrintStream 对应方法引用的源码:
在这里插入图片描述

举例:


import java.util.function.Consumer;public class MethodRefTest {/*** 情况一: 对象 :: 实例方法* Consumer 中的 void accept(T t)* PrintStream 中的 void println(T t)*/public static void main(String[] args) {// 匿名实现接口的方式:Consumer<String> consumer1 = new Consumer<String>() {@Overridepublic void accept(String s) {System.out.println("匿名实现接口的方式: " + s);}};consumer1.accept("Hello World");// Lambda 表达式Consumer<String> consumer2 = s-> System.out.println("Lambda 表达式: " + s);consumer2.accept("Hello World");// 方法引用Consumer<String> consumer = System.out::println;consumer.accept("Hello World");}
}

在这里插入图片描述

举例:

补充:Supplier 接口中的抽象方法 与 Employee 中的方法的源码比较

在这里插入图片描述


import day33.java.Employee;import java.io.PrintStream;
import java.util.function.Consumer;
import java.util.function.Supplier;public class MethodRefTest {/*** Supplier 中的 T get()* Employee 中的String getName()  两者的方法结构是一样的。*/public static void main(String[] args) {// 匿名实现接口的方式:Employee emp = new Employee(1001,"Tom",23,5600);Supplier<String> supplier1 = new Supplier<String>() {@Overridepublic String get() {return emp.getName();}};String regStr1 = supplier1.get();  // 调用其 Supplier 重写的get()抽象方法System.out.println(regStr1);// Lambda 表达式Supplier<String> supplier2 = ()->emp.getName();String regStr2 = supplier2.get();  // 调用其 Supplier 重写的get()抽象方法System.out.println(regStr2);// 方法引用Supplier<String> supplier3 = emp::getName;String regStr3 = supplier3.get();  // 调用其 Supplier 重写的get()抽象方法System.out.println(regStr3);}
}

在这里插入图片描述

6.2 情况2:类::静态方法

举例:

补充: Comparator中的compare 方法与 Integer 中的compare 方法

在这里插入图片描述

package blogs.blog13;import java.util.Comparator;public class MethodRefTest02 {/*** 情况二: 类 :: 静态方法* Comparator 中的 int compare(T t1,T t2)* Integer 中的 int compare(T t1,T t2) 两者之间的结构一致*/public static void main(String[] args) {// 匿名实现接口方式Comparator<Integer> comparator = new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {return Integer.compare(o1,o2);}};int compare = comparator.compare(12, 21);  // 调用其Comparator接口中重写的compare()抽象方法System.out.println(compare);// Lambda 表达式Comparator<Integer> comparator2 = (o1,o2)->Integer.compare(o1,o2);int compare2 = comparator2.compare(12, 21);  // 调用其Comparator接口中重写的compare()抽象方法System.out.println(compare2);// 方法引用Comparator<Integer> comparator3 = Integer::compareTo;int compare3 = comparator3.compare(12, 21);  // 调用其Comparator接口中重写的compare()抽象方法System.out.println(compare3);}
}

在这里插入图片描述

举例:

补充:

在这里插入图片描述


import java.util.function.Function;public class MethodRefTest02 {/*** Function  中的 apply(T t)* Math 中的 Long round(Double d)  // 四舍五入*   两个方法的结构一致。*/public static void main(String[] args) {// 匿名实现接口的方式Function<Double,Long> function = new Function<Double, Long>() {@Overridepublic Long apply(Double d) {return Math.round(d); // 四舍五入}};Long apply = function.apply(12.3);  // 调用Function 接口中的重写的apply()方法System.out.println(apply);// Lambda 表达式Function<Double,Long> function2 = d->Math.round(d);Long apply2 = function2.apply(12.3);  // 调用Function 接口中的重写的apply()方法System.out.println(apply2);// 方法引用Function<Double,Long> function3 = Math::round;Long apply3 = function3.apply(12.3);  // 调用Function 接口中的重写的apply()方法System.out.println(apply3);}
}

在这里插入图片描述

6.3 情况3:类::实例方法

举例:

补充: BiPredicate 中的 boolean test(T t1, T t2) 和 String 中的 boolean t1.equals(t2) 这两者之间的方法的结构也是一致的,这个比较特殊。

在这里插入图片描述


import java.util.function.BiPredicate;public class MethodRefTest03 {/*** BiPredicate 中的 boolean test(T t1, T t2) ;* String 中的 boolean t1.equals(t2)*/public static void main(String[] args) {// 匿名实现接口的方式:BiPredicate<String,String> biPredicate = new BiPredicate<String, String>() {@Overridepublic boolean test(String s, String s2) {return s.equals(s);}};boolean test = biPredicate.test("ab", "ab"); // 调用BiPredicate接口中的 test()抽象方法System.out.println(test);// Lambda 表达式BiPredicate<String, String> biPredicate2 = (s1, s2) -> s1.equals(s2);boolean test2 = biPredicate2.test("ab", "ab"); // 调用BiPredicate接口中的 test()抽象方法System.out.println(test2);// 方法引用BiPredicate<String,String> biPredicate3 = String::equals;boolean test3 = biPredicate3.test("ab", "ab");System.out.println(test3);}
}

在这里插入图片描述

举例:

补充: Function 中的 R apply(T t) 和 Employee 中的 String getName(); 两者方法的结构是一致的

在这里插入图片描述


import java.util.function.Function;public class MethodRefTest03 {/*** Function 中的 R apply(T t)* Employee 中的 String getName(); 两者方法的结构是一致的*/public static void main(String[] args) {Employee employee = new Employee(1001,"Jerry",23,60000);// 匿名实现接口Function<Employee,String> function = new Function<Employee, String>() {@Overridepublic String apply(Employee employee) {return employee.getName();}};String apply = function.apply(employee);  // 调用 Function 接口中的 重写的apply()抽象方法System.out.println(apply);// Lambda 表达式Function<Employee,String> function2 = e->e.getName();  //一个参数,一条语句,一个返回值 () {} 可以省略String apply2 = function2.apply(employee);  // 调用 Function 接口中的 重写的apply()抽象方法System.out.println(apply2);// 方法引用Function<Employee,String> function3 = Employee::getName; // Function 接口中的 apply()抽象方法// 实际上在该重写的抽象方法中调用的是 Employee 类中的 getName()方法。String apply3 = function3.apply(employee);  // 调用 Function 接口中的 重写的apply()抽象方法System.out.println(apply3);}
}

在这里插入图片描述

7. 构造器引用

构造器引用: 与函数式接口相结合,自动与函数式接口方法兼容。

可以把构造器引用赋值给定义的方法,要求:构造器参数列表要与接口中抽象方法的参数列表一致,且方法的返回值即为构造器对应类的对象。 注意: 该接口中重写的抽象方法,仅仅只是调用了其他类中的 构造器 (new 对象)就没有其它的逻辑语句了,只有一条语句才可以使用 构造器引用。

格式如下:

ClassName::new; 类名::new

如下是通过比较:匿名实现类,Lambda 表达式,以及 构造器引用,三者之间的实现同以功能的比较,从而理解 构造器引用

举例1:

补充: Supplier中的 T get() 与 Employee 中的 无参构造器的结构

在这里插入图片描述

package blogs.blog13;import day33.java.Employee;import java.util.function.Supplier;public class ConstructorRefTest {/**** Supplier中的 T get()*/public static void main(String[] args) {// 匿名实现接口Supplier<Employee> supplier = new Supplier<Employee>() {@Overridepublic Employee get() {return new Employee();}};Employee employee = supplier.get();  // 调用 Supplier 接口中重写 get()的抽象方法System.out.println(employee);// Lambda 表达式Supplier<Employee> supplier2 = ()->new Employee();Employee employee2 = supplier2.get();  // 调用 Supplier 接口中重写 get()的抽象方法System.out.println(employee2);// 构造器引用Supplier<Employee> supplier3 = Employee::new;Employee employee3 = supplier3.get();  // 调用 Supplier 接口中重写 get()的抽象方法System.out.println(employee3);}
}

在这里插入图片描述

举例:

补充: Function中的R apply(T t) 与 Employee (int id) 的结构
在这里插入图片描述


import day33.java.Employee;import java.util.function.Function;public class ConstructorRefTest {/**** Function中的R apply(T t)*/public static void main(String[] args) {// 匿名实现接口的方式Function<Integer,Employee> function = new Function<Integer, Employee>() {@Overridepublic Employee apply(Integer integer) {return new Employee(integer);}};Employee apply = function.apply(100);  // 调用function接口中重写的apply()抽象方法System.out.println(apply);// Lambda  表达式Function<Integer,Employee> function2 = id->new Employee(id);Employee apply2 = function2.apply(100);  // 调用function接口中重写的apply()抽象方法System.out.println(apply2);// 构造器引用Function<Integer,Employee> function3 = Employee::new;Employee apply3 = function3.apply(100);  // 调用function接口中重写的apply()抽象方法System.out.println(apply3);}
}

在这里插入图片描述

举例:

补充:

在这里插入图片描述


import day33.java.Employee;import java.util.function.BiFunction;public class ConstructorRefTest {/***BiFunction中的R apply(T t,U u)*/public static void main(String[] args) {// 匿名实现接口的方式BiFunction<Integer,String,Employee> biFunction = new BiFunction<Integer, String, Employee>() {@Overridepublic Employee apply(Integer integer, String s) {return new Employee(integer,s);}};Employee employee = biFunction.apply(100, "Tom"); // 调用 BiFunction 接口中的 apply()抽象方法System.out.println(employee);// Lambda 表达式BiFunction<Integer,String,Employee> biFunction2 = (id,name)->new Employee(id,name);Employee employee2 = biFunction2.apply(100, "Tom"); // 调用 BiFunction 接口中的 apply()抽象方法System.out.println(employee2);// 构造器引用BiFunction<Integer,String,Employee> biFunction3 = Employee::new;Employee employee3 = biFunction3.apply(100, "Tom"); // 调用 BiFunction 接口中的 apply()抽象方法System.out.println(employee3);}
}

在这里插入图片描述

8. 数组引用

数据引用 与构造器引用基本上是一样的,稍微不同的就是在 类型[] 多了个方括号表示数组而已

格式如下:

type[] :: new;// 数据类型[]::new

在这里插入图片描述

举例:

补充: Function 接口源码

在这里插入图片描述

package blogs.blog13;import java.util.Arrays;
import java.util.function.Function;public class ConstructorRefTest02 {/*** Function中的R apply(T t)*/public static void main(String[] args) {// 匿名实现接口 <> 注意泛型不能使用基本数据类型Function<Integer,String[]> function = new Function<Integer, String[]>() {@Overridepublic String[] apply(Integer integer) {return new String[integer];}};String[] apply = function.apply(5);  // 调用 Function 接口中的 重写的apply()抽象方法System.out.println(Arrays.toString(apply));// Lambda 表达式Function<Integer,String[]> function2 = (leng)->new String[leng];String[] apply2 = function2.apply(5);  // 调用 Function 接口中的 重写的apply()抽象方法System.out.println(Arrays.toString(apply2));// 数组引用Function<Integer,String[]> function3 = String[]::new;String[] apply3 = function3.apply(5);  // 调用 Function 接口中的 重写的apply()抽象方法System.out.println(Arrays.toString(apply3));}
}

在这里插入图片描述

9. Lambda 表达式的优缺点:

Lambda表达式的优点很明显,在代码层次上来说,使代码变得非常的简洁,缺点也很明显,代码不易读,可读性比较低。

优点:

  1. 代码简洁,开发迅速。
  2. 方便函数式编程。
  3. 非常容易进行并行计算。
  4. Java 引入 Lambda,改善了集合操作,如集合的排序,遍历,优先级队列自定义大小堆等。

缺点:

  1. 代码可读性变差。
  2. 在非并行计算中,很多计算未必有传统的 for 性能要高。
  3. 不容易进行调试。

10. 总结:

  1. 重点:Lambda 表达式的本质:就是作为接口的实例。简单的说就是对 匿名实现接口的替换。
  2. Lambda 表达式的常见的六种语法格式
  3. 如果一个接口中只有一个抽象方法,(static ,default) 称为函数式接口
  4. 如果我们在某个接口上声明了 @FunctionalInterface 注解,那么编译器就会按照函数式接口的定义来要求该接口,这样如果有两个抽象方法,程序编译就会报错的。所以,从某种意义上来说,只要你保证你的接口中只有一个抽象方法,你可以不加这个注解。加上就会自动进行检测的,保证安全。
  5. 方法引用,本质上就是 Lambda 表达式,而 Lambda 表达式作为函数接口(匿名实现接口重写其抽象方法)的实例,所以方法引用,也是函数式接口的实例。
  6. 方法引用的满足的要求:实现的接口的抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值类型保持一致。换句话说:就是我们接口中重写的抽象方法内部调用的其他类已经实现的方法的,这两者之间的方法的(参数列表)和 return 返回类型要一致,不然不可以使用 方法引用。
  7. 方法引用的常见三种情况:对象::实例方法,类::静态方法,类::实例方法
  8. 构造器引用的要求:构造器参数列表要与接口中抽象方法的参数列表一致,且方法的返回值即为构造器对应类的对象。 注意: 该接口中重写的抽象方法,仅仅只是调用了其他类中的 构造器 (new 对象)就没有其它的逻辑语句了,只有一条语句才可以使用 构造器引用。
  9. 数据引用 与构造器引用基本上是一样的,稍微不同的就是在 类型[] 多了个方括号表示数组而已
  10. Lambda 表达式的优缺点。

11. 最后:

限于自身水平,其中存在的错误,希望大家给予指教,韩信点兵——多多益善,谢谢大家,江湖再见,后会有期 !!!

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_78334.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

EXCEL里的各种奇怪计算问题:数字后面自动多了 0.0001, 数字后面位数变成000,以及一些取整,数学函数

1 公式计算后的数&#xff0c;用只粘贴数值后&#xff0c;后面自动多了 0.0001&#xff0c;导致不再是整数的问题 问题入戏 见第1个8400&#xff0c;计算时就出现了问题&#xff0c;按正常&#xff0c;这里8400应该是整数&#xff0c;而不应该带小数&#xff0c;但是确实就计…

vmware虚拟机与树莓派4B安装ubuntu1804 + ros遇到的问题

如题所示&#xff0c;本人在虚拟机上安装ubuntu1804&#xff0c;可以很容易安装&#xff0c;并且更换系统apt源和ros源&#xff0c;然后安装ros&#xff0c;非常顺利&#xff0c;但是在树莓派4B上安装raspiberry系统就遇到了好多问题。 树莓派我烧录的是这个镜像&#xff1a;ub…

k8s-Kubernetes集群部署

文章目录前言一、Kubernetes简介与架构1.Kubernetes简介2.kubernetes设计架构二、Kubernetes集群部署1.集群环境初始化2.所有节点安装kubeadm3.拉取集群所需镜像3.集群初始化4.安装flannel网络插件5.扩容节点6.设置kubectl命令补齐前言 一、Kubernetes简介与架构 1.Kubernetes…

L - Let‘s Swap(哈希 + 规律)

2023河南省赛组队训练赛&#xff08;四&#xff09; - Virtual Judge (vjudge.net) 约瑟夫最近开发了一款名为Pandote的编辑软件&#xff0c;现在他正在测试&#xff0c;以确保它能正常工作&#xff0c;否则&#xff0c;他可能会被解雇!Joseph通过实现对Pandote上字符串的复制和…

断点调试(debug)

目录 F8案例 ​编辑 debug过程中报错 ​编辑用debug查看方法源码 一层一层查看 Arrays.sort()方法 F9 DebugExercise 介绍&#xff1a;断点调试是指在程序的某一行设置一个断电&#xff0c;调试时&#xff0c;程序运行到这一行就会停住&#xff0c;然后可以一步步往下调试…

项目实战典型案例17——环境混用来带的影响

环境混用来带的影响一&#xff1a;背景介绍背景出现的事故二&#xff1a;思路&方案环境混用的危害如何彻底避免环境混用的问题四&#xff1a;总结五&#xff1a;升华一&#xff1a;背景介绍 本篇博客是对对项目开发中出现的环境混用来带的影响进行的总结并进行的改进。目的…

JAVA后端部署项目三步走

1. JAVA部署项目三步走 1.1 查看 运行的端口 lsof -i:8804 &#xff08;8804 为端口&#xff09; 发现端口25111被监听 1.2 杀死进程,终止程序 pid 为进程号 kill -9 pid 1.3 后台运行jar包 nohup java -jar -Xms128M -Xmx256M -XX:MetaspaceSize128M -XX:MaxM…

基于半车悬架的轴距预瞄与轴间预瞄仿真对比

目录 前言 1. 半车悬架模型 2.轴距预瞄(单点预瞄)和轴间预瞄(两点预瞄)原理与仿真分析 2.1轴距预瞄(单点预瞄) 2.1.1预瞄原理 2.2.轴间预瞄(两点预瞄) 2.2.1预瞄原理 2.3仿真分析 3.总结 前言 对于悬架而言&#xff0c;四个车轮实际的输入信息是受到前后延时以及左右相…

Jetpack Compose 中的重组作用域和性能优化

只有读取可变状态的作用域才会被重组 这句话的意思是只有读取 mutableStateOf() 函数生成的状态值的那些 Composable 函数才会被重新执行。注意&#xff0c;这与 mutableStateOf() 函数在什么位置被定义没有关系。读取操作指的是对状态值的 get 操作。也就是取值的操作。 从一…

路由协议(OSPF、ISIS、BGP)实验配置

目录 OSPF基础实验 建立OSPF邻居 配置虚连接 配置接口的网络类型 配置特殊区域 配置路由选路 配置路由过滤 ISIS基础实验配置 配置ISIS邻居建立 配置认证 配置路由扩散 配置路由过滤 配置定时器 BGP基础实验配置 建立BGP对等体 建立IBGP对等体 建立EBGP对等体…

音频基础知识简述 esp-sr 上手指南

此篇博客先对音频基础知识进行简要叙述&#xff0c;然后帮助读者入门 esp-sr SDK。 1 音频的基本概念 1.1 声音的本质 声音的本质是波在介质中的传播现象&#xff0c;声波的本质是一种波&#xff0c;是一种物理量。 两者不一样&#xff0c;声音是一种抽象的&#xff0c;是声…

第二章Linux操作语法1

文章目录vi和vim常用的三种模式vi和vim快捷键Linux开机&#xff0c;重启用户管理用户信息查询管理who和whoami用户组信息查询管理用户和组的相关文件实用指令集合运行级别帮助指令manhelp文件管理类pwd命令ls命令cd命令mkdir命令rmdir命令rm命令touch命令cp指令mv指令文件查看类…

10.单点登录原理及JWT实现

单点登录原理及JWT实现 一、单点登录效果 首先我们看通过一个具体的案例来加深对单点登录的理解。案例地址&#xff1a;https://gitee.com/xuxueli0323/xxl-sso?_fromgitee_search 把案例代码直接导入到IDEA中 然后分别修改下server和samples中的配置信息 在host文件中配置 …

【Opencv项目实战】图像的像素值反转

文章目录一、项目思路二、算法详解2.1、获取图像信息2.2、新建模板2.3、图像通道顺序三、项目实战&#xff1a;彩图的像素值反转&#xff08;方法一&#xff09;四、项目实战&#xff1a;彩图的像素值反转&#xff08;方法二&#xff09;五、项目实战&#xff1a;彩图转换为灰图…

Spark Catalyst

Spark Catalyst逻辑计划逻辑计划解析逻辑计划优化Catalyst 规则优化过程物理计划Spark PlanJoinSelection生成 Physical PlanEnsureRequirementsSpark SQL 端到端的优化流程&#xff1a; Catalyst 优化器 : 包含逻辑优化/物理优化Tungsten : Spark SQL的优化过程 : 逻辑计划 …

pytorch安装的超级详细教程(没有之一)

一、发展历程 &#xff08;简单介绍&#xff09; (15年)caffe --> (16年)tensorflow1.x --> (17年)keras --> (18年)Tensorflow2.x --> (19年)pytorch。 面向gihub开源项目编程。 向下支持比较好&#xff0c;各个版本之间支持比较好&#xff0c;兼容性强。 版本…

自动驾驶介绍系列 ———— 看门狗

文章目录硬件看门狗软件看门狗差异分析延申窗口看门狗硬件看门狗 硬件看门狗的本质上是一个定时器电路。通常存在一个输入&#xff0c;输入到MCU的RST端。在正常工作状态下&#xff0c;MCU每隔固定时间间隔会输出一个信号给RST端&#xff0c;实现对看门狗端清零。如果在指定的时…

全网最全之接口测试【加密解密攻防完整版】实战教程详解

看视频讲的更详细&#xff1a;https://www.bilibili.com/video/BV1zr4y1E7V5/? 一、对称加密 对称加密算法是共享密钥加密算法&#xff0c;在加密解密过程中&#xff0c;使用的密钥只有一个。发送和接收双方事先都知道加密的密钥&#xff0c;均使用这个密钥对数据进行加密和解…

JAVA开发运维(nginx工作原理)

nginx源码目录结构&#xff1a; . ├── auto 自动检测系统环境以及编译相关的脚本 │ ├── cc 关于编译器相关的编译选项的检测脚本 │ ├── lib nginx编译所需要的一些库的检测脚本 │ ├── os 与平台相关的一些系统参数…

【3.6】链表、操作系统CPU是如何执行程序的、Redis数据类型及其应用

链表 题目题型203. 移除链表元素 - 力扣&#xff08;LeetCode&#xff09;辅助头节点解决移出head问题707. 设计链表 - 力扣&#xff08;LeetCode&#xff09;辅助头节点206. 反转链表 - 力扣&#xff08;LeetCode&#xff09;迭代 / 递归19. 删除链表的倒数第 N 个结点 - 力扣…