详解java中的Lambda表达式

Lambda表达式的前世今生(来历与概述)

Lambda表达式的前世------匿名类

以往,使用单一抽象方法的接口被用作函数类型。 它们的实例表示函数(functions)或行动(actions)。 自从 JDK 1.1 于 1997 年发布以来,创建函数对象的主要手段就是匿名类。匿名类,通俗地讲,就是没有类名,直接通过new关键字创建这个类的实例。下面是匿名类的一个例子:

java.util包中的Comparator接口

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

使用匿名类创建排序的比较方法(强制排序顺序):

Collections.sort(words, new Comparator<String>() {public int compare(String s1, String s2) {return Integer.compare(s1.length(), s2.length());}
});

匿名类适用于需要函数对象的经典面向对象设计模式,特别是策略模式,上面的匿名类是排序字符串的具体策略。 然而,匿名类确实过于冗长。

Lambda表达式的今生

在 Java 8 中,语言形式化了这样的概念,即使用单个抽象方法的接口是特别的,应该得到特别的对待。 这些接口现在称为函数式接口,并且该语言允许你使用lambda 表达式或简称 lambda 来创建这些接口的实例。 Lambdas 在功能上与匿名类相似,但更为简洁。 下面的代码使用 lambdas 替换上面的匿名类。清晰明了:

Collections.sort(words,(s1, s2) -> Integer.compare(s1.length(), s2.length()));

Lambda表达式语法

JDK1.8之后引入的一种语法,他的写法是使用一个->符号,箭头将Lambda表达式分为左右两部分,左边写的是实现的这个接口中的抽象方法中的形参列表,右边就是对抽象方法的处理

(实现的这个接口中的抽象方法中的形参列表) -> 抽象方法的处理

具体写法

因为Lambda表达式的核心就是实现的这个接口中的抽象方法中的形参列表) -> 抽象方法的处理,因此根据形参列表与返回值的不同,Lambda表达式的具体写法也不相同

无返回值有形参的抽象方法

public class MyTest1 {public static void main(String[] args) {MyInterface myInterface = new MyInterface() {@Overridepublic void show(int a, int b) {System.out.println(a + b);}};myInterface.show(20, 30);//50//简写1:方法名可以自己推断出来MyInterface myInterface1 = (int a, int b) -> {System.out.println(a + b);};myInterface1.show(20, 40);//60//简写2:可以省略形参列表中的形参类型MyInterface myInterface2 = (a, b) -> {System.out.println(a + b);//70};myInterface2.show(20, 50);//简写3:如果抽象方法中只有一行代码,可以省略方法体的大括号,当然,如果不止一行,就不能省略MyInterface myInterface3 = (a, b) -> System.out.println(a + b);myInterface3.show(20, 60);//80}
}
public interface MyInterface {public abstract void show(int a,int b);
}

主要特点:

  • 可以省略方法名,IDEA会帮你自动检测方法名;
  • 可以省略方法中的形参类型;
  • 如果对抽象方法的实现逻辑只有一行,可以省略方法体的大括号,当然如果不止一行,就不能省略了;

有返回值的抽象方法

public class MyTest2 {public static void main(String[] args) {MyInterface1 test1 = new MyInterface1() {@Overridepublic int test(int a, int b) {return a - b;}};System.out.println(test1.test(90, 8));//82//简写1:MyInterface1 test2 = (int a, int b) -> {return a - b;};System.out.println(test2.test(20, 10));//10//简写2:MyInterface1 test3 = (a, b) -> {return a - b;};System.out.println(test3.test(30, 10));//20//简写3:这个有返回值的方法,不能直接去掉大括号,还需要去掉return关键字MyInterface1 test4 = (a, b) -> a - b;System.out.println(test4.test(40, 10));//30}
}
public interface MyInterface1 {public abstract int test(int a,int b);
}
  • 有返回值的方法,如果要去掉大括号,还需要去掉return关键字;

有一个形参的抽象方法

public class MyTest3 {public static void main(String[] args) {MyInterface2 myInterface = a -> a-20;myInterface.show(20);}
}
public interface MyInterface2 {public abstract int show(int a);
}
  • 形参列表中只有一个参数,可以去掉形参的括号

Lambda表达式作为参数传递

参数的类型为函数式接口

import java.util.Arrays;
public class MyTest4 {public static void main(String[] args) {Integer[] ints = {89, 67, 23};Arrays.sort(ints, (o1, o2) -> o1-o2);System.out.println(Arrays.toString(ints));//[23, 67, 89]}
}

什么情况下使用

Lambda表达式不是万能的,他需要函数式接口的支持,只有当某个方法需要传入的参数类型为函数式接口或者作为方法返回值时,才能使用。

什么是函数式接口:
函数式接口的定义是: 只包含一个抽象方法的接口,称为函数式接口;
其实我们的Lambda表达式就是对函数式接口的一种简写方式,所以只有是函数式接口,我们才能用Lambda表达式;再换句话说,Lambda表达式需要函数式接口的支持,那函数式接口我们可以自己定义,当然JDK1.8也给我们提供了一些现成的函数式接口;

//自定义一个函数式接口
@FunctionalInterface//此注解用来表明这是一个函数式接口
public interface MyInterFace<T> {
//函数式接口只有一个抽象方法
void getValue(T t);
}
//自定义的函数式接口的Lambda表达式
MyInterFace<String> face=(x)->System.out.println(x);
  • 可以通过 Lambda 表达式来创建该接口的对象,我们可以在任意函数式接口上使用@FunctionalInterface 注解,这样做可以检查它是否是一个函数式接口;
  • 为什么只能有一个抽象方法,如果有多个抽象方法,这个接口不是函数式接口,简写的时候省略了方法名,IDEA不能知道到底重写的是哪一个方法,不能推断出来;
  • 注解写在接口声明上面,如果不报错,就不是函数式接口;
  • JDK1.8之后,提供了很多函数式接口,作为参数传递;

常用的函数式接口

Java 8在java.util.function包下预定义了大量的函数式接口供我们使用

我们重点学习下面4个接口

  • Supplier接口
  • Consumer接口
  • Predicate接口
  • Function接口

Supplier接口

Supplier< T >:包含一个无参的方法

  • T get():获得结果
  • 该方法不需要参数,他会按照某种实现逻辑(由Lambda表达式实现)返回一个数据
  • Supplier< T >接口也被称为生产型接口,如果我们指定了接口的泛型是什么类型,那么接口中的get方法就会产生什么类型的数据供我们使用
import java.util.function.Supplier;public class SupplierDemo {public static void main(String[] args) {String s = getString(()->"张三");int i = getInteger(()->18);System.out.println(s+","+i);}//定义一个方法,返回一个字符串数据private static String getString(Supplier<String> sup){return sup.get();}//定义一个方法,返回一个整数数据private static Integer getInteger(Supplier<Integer> sup){return sup.get();}
}

Consumer接口

Consumer< T >:包含两个方法

  • void accept(T t):对给定的参数执行此操作
  • default Consumer < T > andThen(Consumer after):返回一个组合的Consumer,依次执行此操作,然后执行after操作
  • Consumer< T >接口也被称为消费型接口,它消费的数据类型由泛型指定
import java.util.function.Consumer;public class ConsumerDemo {public static void main(String[] args) {operatorString("张三", (s) -> System.out.println(s));operatorString("张三", (s) -> System.out.println(s), (s)-> System.out.println(new StringBuilder(s).reverse().toString()));}//定义一个方法,消费一个字符串数据private static void operatorString(String name, Consumer<String> con) {con.accept(name);}//定义一个方法,用不同的方式消费同一个字符串两次private static void operatorString(String name, Consumer<String> con1,Consumer<String> con2) {
//        con1.accept(name);
//        con2.accept(name);//返回一个组合的Consumercon1.andThen(con2).accept(name);}
}

Predicate接口

Predicate< T >:常用的四个方法

  • boolean test(T t):对给定的参数进行判断(判断逻辑由Lambda表达式实现),返回一个布尔值
  • default Predicate< T > negate():返回一个逻辑的否定,对应逻辑非
  • default Predicate< T > and():返回一个组合判断,对应短路与
  • default Predicate< T > or():返回一个组合判断,对应短路或
  • isEqual():测试两个参数是否相等
  • Predicate< T >:接口通常用于判断参数是否满足指定的条件
import java.util.function.Predicate;public class ConsumerTest {public static void main(String[] args) {boolean string = chenkString("张三", s -> s.equals("张三"));System.out.println(string);boolean hello = chenkString("hello", s -> s.length() > 8, s -> s.length() < 18);System.out.println(hello);}//判定给定的字符串是否满足要求
//    private static boolean chenkString(String s, Predicate<String> pre){
//        return pre.test(s);
//    }private static boolean chenkString(String s, Predicate<String> pre){return pre.negate().test(s);}//    private static boolean chenkString(String s, Predicate<String> pre, Predicate<String> pre1){
//        return pre.and(pre1).test(s);
//    }private static boolean chenkString(String s, Predicate<String> pre, Predicate<String> pre1){return pre.or(pre1).test(s);}
}

Function接口

Runction<T,R>:常用的两个方法

  • R apply(T t):将此函数应用于给定的参数
  • default< V >:Function andThen(Function after):返回一个组合函数,首先将该函数应用于输入,然后将after函数应用于结果
  • Function<T,R>:接口通常用于对参数进行处理,转换(处理逻辑由Lambda表达式实现),然后返回一个新值
import java.util.function.Function;public class ConsumerTest {public static void main(String[] args) {convert("18", s -> Integer.parseInt(s));convert(20, integer -> String.valueOf(integer + 18));convert("245", s -> Integer.parseInt(s), integer -> String.valueOf(integer + 18));}   //定义一个方法,把一个字符串转换成int类型,在控制台输出private static void convert(String s, Function<String, Integer> fun) {int i = fun.apply(s);System.out.println(i);}    //定义一个方法,把int类型数据加上一个整数之后,转换为字符串在控制台输出private static void convert(int i, Function<Integer, String> fun) {String s = fun.apply(i);System.out.println(s);}   //定义一个方法,把一个字符串转换int类型,把int类型的数据加上一个整数后,转换成字符串在控制台输出private static void convert(String s, Function<String, Integer> fun1, Function<Integer, String> fun2) {String s1 = fun2.apply(fun1.apply(s));System.out.println(s1);}
}

方法引用

先来看一下什么是方法引用:

方法引用其实是Lambda表达式的另一种写法,当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用
注意: 实现抽象方法的参数列表,必须与方法引用方法的参数列表保持一致!
方法引用:使用操作符::将方法名和对象或类的名字分隔开来,三种主要使用情况为:

对象::实例方法
类::静态方法
类::实例方法

对象::实例方法

import java.util.function.Consumer;public class MyTest {public static void main(String[] args) {Consumer<String> consumer = new Consumer<String>() {@Overridepublic void accept(String s) {System.out.println(s);}};consumer.accept("aaaaaaaaaaaaaa");//aaaaaaaaaaaaaa//简写1:Consumer<String> consumer1 = (String s) -> {System.out.println(s);};consumer1.accept("abc");//abc//简写2:Consumer<String> consumer2 = (s) -> System.out.println(s);consumer2.accept("bcd");//bcd//简写3:Consumer<String> consumer3 = System.out::println;consumer3.accept("abc");//abc}
}

为什么可以写成上述方式?
因为:System.out.println(s);与void accept(String s)一样,都是使用s作为参数,返回值是void,因此就可以简写为简写3;

类::静态方法

import java.util.function.BinaryOperator;public class MyTest1 {public static void main(String[] args) {BinaryOperator<Double> operator = new BinaryOperator<Double>(){@Overridepublic Double apply(Double o, Double o2) {return Math.max(o,o2);}};System.out.println(operator.apply(2.13, 3.12));//3.12BinaryOperator<Double> operator2 = (o, o2) -> Math.max(o,o2);System.out.println(operator2.apply(2.13, 3.12));//3.12BinaryOperator<Double> operator3 = Math::max;Double max = operator3.apply(5.0, 20.0);System.out.println(max);//20.0}
}

因为Math.max()所需要的参数以及返回值与重写的accpet()一样,因此可以简写为类::静态方法

下面再举一个例子

import java.util.Comparator;
public class MyTest2 {public static void main(String[] args) {Comparator<Integer> comparator = new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {return Integer.compare(o1,o2);}};System.out.println(comparator.compare(20, 12));//1Comparator<Integer> comparator1 = Integer::compareTo;System.out.println(comparator1.compare(20, 12));//1}
}

类::实例方法

import java.util.Comparator;
public class MyTest2 {public static void main(String[] args) {Comparator<String> comparator = new Comparator<String>() {@Overridepublic int compare(String o1, String o2) {return o1.compareTo(o2);}};System.out.println(comparator.compare("20", "12"));//1Comparator<String> comparator1 = String::compareTo;System.out.println(comparator1.compare("20", "12"));//1}
}

为什么可以这样写?、
传递过来的两个参数,一个作为调用者,一个作为参数,这时候,使用类::实例方法简写;

构造引用

格式:ClassName::new
与函数式接口相结合,自动与函数式接口中方法兼容。可以把构造器引用赋值给定义的方法,与构造器参数列表要与接口中抽象方法的参数列表一致!

public class MyTest4 {public static void main(String[] args) {Student student = new Student("张三", 23);BiFunction<String, Integer, Student> function = new BiFunction<String, Integer, Student>() {@Overridepublic Student apply(String s, Integer integer) {return student;}};BiFunction<String, Integer, Student> function1 = (s, integer) -> student;BiFunction<String, Integer, Student> function2 = Student::new;}
}

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

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

相关文章

【MySQL】超详细-基础操作

数据库定义 数据库是一类软件&#xff0c;用来管理数据&#xff0c;组织数据&#xff1b; 关系型数据库MySQL&#xff08;Oracle,SQL Server,SQLite&#xff09;以表格形式组织数据&#xff0c;数据格式要求严格&#xff1b;非关系型数据库Redis&#xff08;MongoDB,HBase&…

数据结构与算法-冒泡排序

引言 在数据结构与算法的世界里&#xff0c;冒泡排序作为基础排序算法之一&#xff0c;以其直观易懂的原理和实现方式&#xff0c;为理解更复杂的数据处理逻辑提供了坚实的入门阶梯。尽管在实际应用中由于其效率问题不常被用于大规模数据的排序任务&#xff0c;但它对于每一位初…

【C++】set、multiset与map、multimap的使用

目录 一、关联式容器二、键值对三、树形结构的关联式容器3.1 set3.1.1 模板参数列表3.1.2 构造3.1.3 迭代器3.1.4 容量3.1.5 修改操作 3.2 multiset3.3 map3.3.1 模板参数列表3.3.2 构造3.3.3 迭代器3.3.4 容量3.3.5 修改操作3.3.6 operator[] 3.4 multimap 一、关联式容器 谈…

Hololens 2应用开发系列(1)——使用MRTK在Unity中设置混合现实场景并进行程序模拟

Hololens 2应用开发系列&#xff08;1&#xff09;——使用MRTK在Unity中进行程序模拟 一、前言二、创建和设置MR场景三、MRTK输入模拟的开启 一、前言 在前面的文章中&#xff0c;我介绍了Hololens 2开发环境搭建和项目生成部署等相关内容&#xff0c;使我们能生成一个简单Ho…

Redis 之九:Spring Data Redis -- Redis Template 用法

SpringData Redis Spring Data Redis 是 Spring Data 项目的一部分&#xff0c;它为 Java 应用程序提供了一种便捷的方式来与 Redis 数据库进行交互。 Spring Data Redis 提供了对 Redis 的抽象封装&#xff0c;使得开发者能够以面向对象的方式操作 Redis&#xff0c;并简化了 …

matlab 写入格式化文本文件

目录 一、save函数 二、fprintf函数 matlab 写入文本文件可以使用save和fprintf函数 save输出结果: fprintf输出结果: 1.23, 2.34, 3.45 4.56, 5.67, 6.78 7.89, 8.90, 9.01 可以看出fprintf输出结果更加人性化,符合要求,下面分别介绍。 一、save函数 …

linux系统Jenkins工具介绍

Jenkins概念介绍 Jenkins概念Jenkins目的特性产品发布流程 Jenkins概念 Jenkins是一个功能强大的应用程序&#xff0c;允许持续集成和持续交付项目&#xff0c;无论用的是什么平台。这是一个免费的源代码&#xff0c;可以处理任何类型的构建或持续集成。集成Jenkins可以用于一些…

MQL5-MT5连接上国内期货

主要原因是昨天在学习MACD时发现给的基础代码感觉不对&#xff0c;但无法证明&#xff0c;因为MT5接的都是外汇交易&#xff0c;数据和国内的文华啥的全对不上&#xff0c;便找了一些国内接CTP的&#xff0c;直接写代码有点麻烦&#xff0c;虽然之前对接过国内CTP的东西&#x…

AI入门笔记(三)

神经网络是如何工作的 神经网络又是如何工作的呢&#xff1f;我们用一个例子来解释。我们看下面这张图片&#xff0c;我们要识别出这些图片都是0并不难&#xff0c;要怎么交给计算机&#xff0c;让计算机和我们得出同样的结果&#xff1f;难点就在于模式识别的答案不标准&…

十二、Nacos源码系列:Nacos配置中心原理(四)- RefreshEvent 事件处理

前面文章&#xff0c;我们说到回调监听器的方法中&#xff0c;主要就是发布了一个RefreshEvent事件&#xff0c;这个事件主要由 SpringCloud 相关类来处理。今天我们继续分析后续的流程。 RefreshEvent 事件会由 RefreshEventListener 来处理&#xff0c;该 listener 含有一个 …

Object类方法

toString(): 返回对象的字符串表示形式。默认情况下&#xff0c;返回对象的类名和哈希码的十六进制表示。 equals(Object obj): 比较两个对象是否相等。默认情况下&#xff0c;这个方法比较的是两个对象的引用是否相同&#xff0c;但是通常会在子类中重写这个方法以实现自定义…

武器大师——操作符详解(下)

目录 六、单目操作符 七、逗号表达式 八、下标引用以及函数调用 8.1.下标引用 8.2.函数调用 九、结构体 9.1.结构体 9.1.1结构的声明 9.1.2结构体的定义和初始化 9.2.结构成员访问操作符 9.2.1直接访问 9.2.2间接访问 十、操作符的属性 10.1.优先性 10.2.结合性 …

sql基本语法+实验实践

sql语法 注释&#xff1a; 单行 --注释内容# 注释内容多行 /* 注释内容 */数据定义语言DDL 查询所有数据库 show databases;注意是databases而不是database。 查询当前数据库 select database();创建数据库 create database [if not exists] 数据库名 [default charset 字符…

备战蓝桥杯Day22 - 计数排序

计数排序问题描述 对列表进行排序&#xff0c;已知列表中的数范围都在0-100之间。设计时间复杂度为O(n)的算法。 比如列表中有一串数字&#xff0c;2 5 3 1 6 3 2 1 &#xff0c;需要将他们按照从小到大的次序排列&#xff0c;得到1 1 2 2 3 3 5 6 的结果。那么此时计数排序是…

一:面试流程

面试 项目介绍功能测试接口测试性能测试测试用例 项目介绍 南网智搜是南方电网公司研发的搜索引擎&#xff0c;主要场景Web 端场景有搜索频道、个人中心、和一些积分活动等&#xff0c;我在里面主要负责功能测试&#xff0c;接口测试&#xff0c;性能测试&#xff0c;压力测试…

Jetson Xavier NX 开发板Ubuntu18.04 安装arduino IDE详细步骤

Jetson 平台是arch架构&#xff0c;官网上面几乎都是x86或者arm64的这两种错误版本都存在匹配问题无法使用&#xff0c;不要下载不要下载&#xff01; uname -a #版本查询1.正确下载打开方式 https://downloads.arduino.cc/arduino-1.8.19-linuxaarch64.tar.xz选择自己想要下…

LeetCode #104 二叉树的最大深度

104. 二叉树的最大深度 题目 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;3 示例 2&#xff1a; 输入&#xff1a;root [1,null,2] 输出&#xff1a;2 分析 …

【Godot4自学手册】第十九节敌人的血量显示及掉血特效

这一节&#xff0c;我主要学习敌人的血量显示、掉血显示和死亡效果。敌人的血量显示和主人公的血量显示有所不同&#xff0c;主要是在敌人头顶有个红色的血条&#xff0c;受到攻击敌人的血条会减少&#xff0c;并且有掉血数量的文字显示&#xff0c;效果如下&#xff1a; 一、…

《中华人民共和国消防法》(2021年修订版)解读

单选题&#xff08;共7题&#xff0c;每题5分&#xff09; 1、举办大型群众性活动&#xff0c;承办人应当依法向&#xff08;&#xff09;申请安全许可。 正确答案&#xff1a;B、公安机关 2、违反消防安全规定进入生产、储存易燃易爆危险品场所的&#xff0c;情节严重的要处…

基于springboot+vue的医院后台管理系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…