Lambda表达式
一、什么是Lambda表达式🍉
Lambda 表达式(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。Lambda表达式可以表示闭包,和传统数学上的意义有区别。
Java 8的一个大亮点是引入Lambda表达式,使用它设计的代码会更加简洁。当开发者在编写Lambda表达式时,也会随之被编译成一个函数式接口。下面这个例子就是使用Lambda语法来代替匿名的内部类,代码不仅简洁,而且还可读。
二、为什么使用Lambda表达式🍉
package demo1;
public class Text {
public static void main(String[] args) {
//第一种方式
//创建线程对象 通过实现Runnable接口的方式创建线程任务
Thread thread = new Thread(new My());
//开启线程
thread.start();
//第二种方式
//创建线程任务 通过匿名内部类的方式创建
Runnable runnable = new Runnable(){
@Override
public void run() {
System.out.println("通过匿名内部类实现线程任务");
}
};
//创建线程
Thread thread1 = new Thread(runnable);
//开启线程
thread1.start();
//第三种方式
//创建线程 通过lambda表达式方式创建线程任务
Thread thread2 = new Thread(() -> {
System.out.println("这是lambda表达式完成线程任务");
});
//开启线程
thread2.start();
}
}
class My implements Runnable{
@Override
public void run() {
System.out.println("开启线程任务");
}
}
分析:
- Thread类需要一个Runnable接口作为参数,其中的抽象方法run方法是用来指定线程任务内容的核心
- 为了指定run方法体,不得不需要Runnable的实现类
- 为了省去定义一个Runnable 的实现类,不得不使用匿名内部类
- 必须覆盖重写抽象的run方法,所有的方法名称,方法参数,方法返回值不得不都重写一遍,而且不能出错,
- 而实际上,我们只在乎方法体中的代码.
我们可以使用lambda表达式来完成上面的功能。
三、Lambda表达式的语法🍉
Lambda省去了面向对象的条条框框,Lambda的标准格式由3个部分组成:
(参数列表) -> {}
():参数列表
->:连接符 连接的是参数以及方法体。
{}: 方法体。
四、Lambda表达式的使用🍉
1.无参无返回值Lambda表达式🍎
package demo2;
public class Text2 {
public static void main(String[] args) {
//主函数调用fun方法
//第一种:创建UserService接口的实现类,并创建该实现类对象
//第二种:匿名内部类的方式
UserService userService = new UserService() {
@Override
public void show() {
System.out.println("这是匿名内部类的shou方法的实现");
}
};
fun(userService);
//第三种lambda表达式:--该接口必须为函数式接口
//无参无返回值
UserService userService1 = ()->{
System.out.println("lambda表示的show方法");
};
fun(userService1);
}
public static void fun(UserService userService) {
userService.show();
}
}
//函数式接口-->里面有且仅有一个抽象方法,--只有这种接口才能使用lambda表达式
interface UserService {
public void show();
}
2.有参有返回值Lambda表达式🍎
package demo3;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class Text3 {
public static void main(String[] args) {
//需求对集合中的元素按照年龄排序,小到大
//创建集合
ArrayList<Person> people = new ArrayList<>();
//为集合添加数据
people.add(new Person("张三",15));
people.add(new Person("李四",1));
people.add(new Person("王五",18));
people.add(new Person("周六",20));
people.add(new Person("田七",21));
System.out.println(people);
//Collections:集合工具类。--匿名内部类
//用匿名内部类的方式规定排序方式
Comparator<Person> comparator = new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
//前后两个值进行比较 o1-o2 等于0 表示相同 大于0表示o1大于o2
return o1.getAge()- o2.getAge();
}
};
//通过Collections集合工具类中sort方法将集合进行排序
Collections.sort(people,comparator);
System.out.println(people);
//使用lambda表达式的方式规定排序方式
//可以理解为简化匿名内部类
//lambda表达式完整写法
Comparator<Person> comparator1 =(o1, o2) -> {
return o1.getAge()-o2.getAge();
};
//lambda表达式简写版
Comparator<Person> comparator2=(o1, o2) -> o1.getAge()- o2.getAge();
}
}
class Person{
private String name;
private Integer age;
public Person() {
}
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public Integer getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(Integer age) {
this.age = age;
}
public String toString() {
return "Person{name = " + name + ", age = " + age + "}";
}
}
3.Lambda表达式的省略写法🍎
在lambda表达式的标准写法基础上,可以使用省略写法的规则为:
- 小括号内的参数类型可以省略[]
- 如果小括号内有且仅有一个参数,则小括号可以省略
- 如果大括号内有且仅有一个语句,可以同时省略大括号,return 关键字及语句分号。
package demo4;
public class Text5 {
public static void main(String[] args) {
//lambda表达式
//将USB接口传入的参数变成大写 tpUpperCase 将字符串转换为大写
USB u = str -> str.toUpperCase();
//匿名内部类对接口中抽象方法的个数没有任何要求
USB u2=new USB() {
@Override
public String toUpper(String str) {
return null;
}
};
fun(u);
}
public static void fun(USB usb){
String hello = usb.toUpper("hello");
System.out.println(hello);
}
}
interface USB{//函数式接口
public String toUpper(String str);
}
package demo4;
public class Text4 {
public static void main(String[] args) {
U01 u = a -> System.out.println("你好阿娇:"+a);
fun(u);
//lambda表达式在省略return时默认就会把最后一条语句作为返回结果
U02 u1 =a ->a*2;
fun2(u1);
}
public static void fun(U01 u01){
u01.show(15);
}
public static void fun2(U02 u02){
int print = u02.print(25);
System.out.println(print);
}
}
interface U02{
public int print(int a );
}
interface U01{
public void show(int a );
}
4.Lambda表达式使用的前提🍎
Lambda表达式的语法是非常简洁的,但是Lambda表达式不是随便使用的,使用时有几个条件要特别注意
- 方法的参数或局部变量类型必须为接口才能使用Lambda
- 接口中有且仅有一个抽象方法(@FunctionalInterface)
后面我们使用lambda表达式主要核心使用再Stream流中。
五、内置函数式接口🍉
1. 什么是内置函数式接口🍎
要想使用lambda表达式它的前提就是必须是函数式接口。
本人理解:
函数式接口就是接口中有且只有一个抽象方法
内置函数式接口就是jdk封装好的函数式接口,供编程人员使用的接口
这样在编程中使用lambda表达式时可以不需要自己创建函数式接口直接调用系统封装好的接口就可以了
2.内置函数式接口的由来🍎
public class Test {
public static void main(String[] args) {
Operation o=arr->{
int sum=0;
for(int s:arr){
sum+=s;
}
return sum;
};
fun(o);
}
public static void fun(Operation operation){
int[] arr={1,2,3,4};
int s = operation.getSum(arr);
System.out.println("数组的和:"+s);
}
}
//自己创建的函数式接口
@FunctionalInterface
interface Operation{
public int getSum(int[] arr);
}
分析:
我们知道使用Lambda表达式的前提是需要有函数式接口,而Lambda表达式使用时不关心接口名,抽象方法名。只关心抽象方法的参数列表和返回值类型。因此为了让我们使用Lambda表达式更加的方便,在JDK中提供了大量常用的函数式接口. 大多数无需自己再定义函数式接口,而可以直接使用jdk内置的函数式接口。分成四类。
3.函数式接口的使用🍎
消费型函数式接口Consumer🥝
适合有参数,但是没有返回值的。
public static void fun(Consumer<Double> consumer,Double money){
//调用Consumer消费型函数式接口中的抽象方法 accept
consumer.accept(money);
}
//通过lembda表达式创建消费型函数式接口的实现对象 重写抽象方法
Consumer<Double> consumer = t->{
System.out.println("消费型函数接口");
};
//消费型函数式接口有参无返回值
fun(consumer,200.0);
供给型函数式接口—Supplier🥝
无参,需要返回值的接口类。
public static void fun2(Supplier<Integer> supplier){
//调用Supplier供给型函数式接口中的抽象方法 get
Integer a = supplier.get();
System.out.println("结果:"+a);
}
//通过lembda表达式创建供给型函数式接口的实现对象 重写抽象方法
//new Random().nuxInt 表示生成随机数
Supplier<Integer> supplier=()->new Random().nextInt(10);
//供给型函数式接口无参有返回值
fun2(supplier);
函数型函数式接口—Function<T,R>🥝
T: 参数的泛型
R:返回值的泛型。
public static void fun3(Function<int[],Integer> f){
//调用Function函数型函数式接口中的抽象方法 apply
//创建数组arr
int[] arr={1,2,3,4};
//将数组当作参数传入apply方法中
int s=f.apply(arr);
System.out.println("结果:" + s);
}
//通过lembda表达式创建函数型函数式接口的实现对象 重写抽象方法
Function<int[],Integer> function = t->{
int sum = 0;
for (int i : t) {
sum+=i;
}
return sum;
};
//函数型函数式接口有参有返回值
fun3(function);
断言型函数式接口–Predicate🥝
T: 参数
boolean:返回值类型。
public static void fun4(Predicate<String> predicate,String name){
//调用Predicate断言型函数式接口中的抽象方法 test
boolean test = predicate.test(name);
System.out.println("是否成年:" + test);
}
//通过lembda表达式创建断言型函数式接口的实现对象 重写抽象方法
Predicate<String> predicate =t->t.length()>3;
//断言型函数式接口有参返回值为布尔型
fun4(predicate,"万文卿");
内置函数式接口汇总
完整代码
package demo6;
import java.util.Random;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
public class Text6 {
public static void main(String[] args) {
//通过lembda表达式创建消费型函数式接口的实现对象 重写抽象方法
Consumer<Double> consumer = t->{
System.out.println("消费型函数接口");
};
//消费型函数式接口有参无返回值
fun(consumer,200.0);
//通过lembda表达式创建供给型函数式接口的实现对象 重写抽象方法
//new Random().nuxInt 表示生成随机数
Supplier<Integer> supplier=()->new Random().nextInt(10);
//供给型函数式接口无参有返回值
fun2(supplier);
//通过lembda表达式创建函数型函数式接口的实现对象 重写抽象方法
Function<int[],Integer> function = t->{
int sum = 0;
for (int i : t) {
sum+=i;
}
return sum;
};
//函数型函数式接口有参有返回值
fun3(function);
//通过lembda表达式创建断言型函数式接口的实现对象 重写抽象方法
Predicate<String> predicate =t->t.length()>3;
//断言型函数式接口有参返回值为布尔型
fun4(predicate,"万文卿");
}
public static void fun4(Predicate<String> predicate,String name){
//调用Predicate断言型函数式接口中的抽象方法 test
boolean test = predicate.test(name);
System.out.println("是否成年:" + test);
}
public static void fun3(Function<int[],Integer> f){
//调用Function函数型函数式接口中的抽象方法 apply
//创建数组arr
int[] arr={1,2,3,4};
//将数组当作参数传入apply方法中
int s=f.apply(arr);
System.out.println("结果:" + s);
}
public static void fun2(Supplier<Integer> supplier){
//调用Supplier供给型函数式接口中的抽象方法 get
Integer a = supplier.get();
System.out.println("结果:"+a);
}
public static void fun(Consumer<Double> consumer,Double money){
//调用Consumer消费型函数式接口中的抽象方法 accept
consumer.accept(money);
}
}
六、方法引用🍉
特殊的lambda表达式,它是对lambda表达式的一种简写方式。
1.方法引用的由来🍎
package demo7;
import java.util.function.Consumer;
public class Text7 {
public static void main(String[] args) {
//通过lembda表达式创建Consumer消费型函数式接口方式
Consumer<int[]> c=t->{
int sum=0;
for (int i : t) {
sum+=i;
}
System.out.println("数组的和:"+sum);
};
//通过调用方法创建Consumer消费型函数式接口方式
Consumer<int[]> c1=t->Text7.sum(t);
//通过方法引用的方式创建Consumer消费型函数式接口方式
Consumer<int[]> c2 = Text7::sum;
fun(c2);
}
public static void fun(Consumer<int[]> consumer){
int[] arr={1,2,3,4};
consumer.accept(arr);
}
//求和方法
public static void sum(int[] arr){
int sum=0;
for (int i : arr) {
sum+=i;
}
System.out.println("数组的和:"+sum);
}
}
通过上面的代码我们可以发现,在我们使用Lambda表达式时,重写逻辑方法,当已经有写好的逻辑方法,我们可以直接引用写好的逻辑方法去写Lambda表达式,这样我们的代码会更简洁明了
2.方法引用的类型🍎
静态方法引用🥝
(args)->类名.静态方法(args).
当lambda表达式中方法体,只有一条语句,而这条语句是类名.静态方法。而静态方法的参数和lambda的参数一致时。
类名::静态方法;
package demo7;
import java.util.function.Consumer;
public class Text7 {
public static void main(String[] args) {
//通过lembda表达式创建Consumer消费型函数式接口方式
Consumer<int[]> c=t->{
int sum=0;
for (int i : t) {
sum+=i;
}
System.out.println("数组的和:"+sum);
};
//通过调用方法创建Consumer消费型函数式接口方式
Consumer<int[]> c1=t->Text7.sum(t);
//通过方法引用的方式创建Consumer消费型函数式接口方式
Consumer<int[]> c2 = Text7::sum;
fun(c2);
}
public static void fun(Consumer<int[]> consumer){
int[] arr={1,2,3,4};
consumer.accept(arr);
}
//求和方法
public static void sum(int[] arr){
int sum=0;
for (int i : arr) {
sum+=i;
}
System.out.println("数组的和:"+sum);
}
}
实例方法引用🥝
(args) -> inst.instMethod(args)
实例方法引用,顾名思义就是调用已经存在的实例的方法,与静态方法引用不同的是类要先实例化,静态方法引用类无需实例化,直接用类名去调用。
//创建一个对象
Student student = new Student("张三", 25);
//通过内置的供给型函数式接口创建lambda表达式
Supplier<String> s3 = ()->student.getName();
//实例方法引用 s3可以简写成s4
Supplier<String> s4 = student::getName;
class Student{
private String name;
private Integer age;
public Student() {
}
public Student(String name, Integer age) {
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public Integer getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(Integer age) {
this.age = age;
}
public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}
}
对象方法引用🥝
lambda: (inst,args)->inst.普通方法(args): -------->类名::普通方法
若Lambda参数列表中的第一个参数是实例方法的参数调用者,而第二个参数是实例方法的参数时,可以使用对象方法引用。
//需求:判断两个字符串是否一致
//第一种方式
//根据lambda表达式创建内置函数型函数式接口 重写抽象方法 完成需求对两个字符串进行比较
//BiFunction 是Function接口的子接口 <> 泛型中需要三个参数 前两个参数表示需要比较的字符串 最后一个参数表示返回值
BiFunction<String,String,Boolean> biFunction = (t1,t2)->t1.equals(t2);
//传入两个字符串进行比较
Boolean apply = biFunction.apply("hello", "hello");
System.out.println(apply);
//第二种方式
//通过对象方法引用的方式创建内置函数型函数式接口 重写抽象方法
//第二种方式是对第一种方式的Lambda表达式的简写
BiFunction<String,String,Boolean> biFunction1=String::equals;
public static void show(BiFunction<String,String,Boolean> biFunction){
Boolean apply = biFunction.apply("hello", "hello");
System.out.println(apply);
}
构造方法引用🥝
(args) -> new 类名(args)------构造方法引用: 类名::new
//通过内置的供给型函数式接口创建lambda表达式
BiFunction<String,Integer,Student> s = (str,i)->new Student();
//构造方法引用
BiFunction<String,Integer,Student> s2 = Student::new;
总结:
- 静态方法引用 类名::静态方法 lambda表达式: (参数)->类名.静态方法(参数)
- 实例方法引用:对象::实例方法 lambda表达式: (参数)->对象.实例方法(参数)
- 对象方法引用:类名::实例方法 lambda表达式: (参数1,参数2…)->参数1.实例方法(参数2…)
- 构造方法引用: 类名::new lambda表达式: (参数)->new 类名(参数);
完整代码:
package demo9;
import java.util.function.BiFunction;
import java.util.function.Supplier;
public class Text9 {
public static void main(String[] args) {
//通过内置的供给型函数式接口创建lambda表达式
BiFunction<String,Integer,Student> s = (str,i)->new Student();
//构造方法引用
BiFunction<String,Integer,Student> s2 = Student::new;
//创建一个对象
Student student = new Student("张三", 25);
//通过内置的供给型函数式接口创建lambda表达式
Supplier<String> s3 = ()->student.getName();
//实例方法引用 s3可以简写成s4
Supplier<String> s4 = student::getName;
//需求:判断两个字符串是否一致
//第一种方式
//根据lambda表达式创建内置函数型函数式接口 重写抽象方法 完成需求对两个字符串进行比较
//BiFunction 是Function接口的子接口 <> 泛型中需要三个参数 前两个参数表示需要比较的字符串 最后一个参数表示返回值
BiFunction<String,String,Boolean> biFunction = (t1,t2)->t1.equals(t2);
//传入两个字符串进行比较
Boolean apply = biFunction.apply("hello", "hello");
System.out.println(apply);
//第二种方式
//通过对象方法引用的方式创建内置函数型函数式接口 重写抽象方法
//第二种方式是对第一种方式的Lambda表达式的简写
BiFunction<String,String,Boolean> biFunction1=String::equals;
}
public static void show(BiFunction<String,String,Boolean> biFunction){
Boolean apply = biFunction.apply("hello", "hello");
System.out.println(apply);
}
}
class Student{
private String name;
private Integer age;
public Student() {
}
public Student(String name, Integer age) {
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public Integer getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(Integer age) {
this.age = age;
}
public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}
}