Java基础---lambda表达式

news2024/9/23 23:35:22

一、为什么要引入lambda表达式

        lambda 表达式是一个可传递的代码块 可以在以后执行一次或多次
        在介绍lambda表达式之前,我们看一下,以前,我们对于一个问题的通常写法。
        
        假设你已经了解了如何按指定时间间隔完成工作,当然不了解也没关系,只是作为例子说明。 将这个工作放在一个 ActionListener 的 actionPerformed 方法中
class Worker implements ActionListener
{
    public void actionPerformed(ActionEvent event)
    {
        // do some work
    }
}
        想要反复执行这个代码时, 可以构造 Worker 类的一个实例 然后把这个实例提交到一个 Timer 对象 这里的重点是 actionPerformed 方法包含希望以后执行的代码
        或者可以考虑如何用一个定制比较器完成排序。 如果想按长度而不是默认的字典顺序对
字符串排序 可以向 sort 方法传人一个 Comparator 对象
class LengthComparator implements Comparator<String>
{
    public int compare(String first, String second)
    {
        return first.length() - second.length();
    }
}
        这两个例子有一些共同点, 都是将一个代码块传递到某个对象 一个定时器 或者一个 sort 方法)。这个代码块会在将来某个时间调用。
        到目前为止, Java 中传递一个代码段并不容易 不能直接传递代码段 。   Java 是一种面
向对象语言 所以必须构造一个对象 这个对象的类需要有一个方法能包含所需的代码。
        在其他语言中, 可以直接处理代码块 Java 设计者很长时间以来一直拒绝增加这个特性 。 毕竟, Java 的强大之处就在于其简单性和一致性 如果只要一个特性能够让代码稍简洁一些 , 就把这个特性增加到语言中, 这个语言很快就会变得一团糟 无法管理 。 不过 在另外那些 语言中, 并不只是创建线程或注册按钮点击事件处理器更容易 它们的大部分 API 都更简单 、 更一致而且更强大。 Java 也可以编写类似的 API 利用类对象实现特定的功能 不过这种 API 使用可能很不方便

二、lambda表达式的语法

        再来考虑上面讨论的排序例子。 我们传入代码来检查一个字符串是否比另一个字符串短。 这里要计算
        first. length()  - second . length()
        first second 是什么 它们都是字符串 Java 是一种强类型语言 所以我们还要指定它 们的类型:
        (String first, String second)
                -> first.length() - second.length()
        这就是一个lambda表达式。lambda 表达式就是一个代码块 以及必须传入代码的变量规范。
        你已经见过 Java 中的一种 lambda 表达式形式 参数 箭头 - > ) 以及一个表达式 如果代码要完成的计算无法放在一个表达式中, 就可以像写方法一样 把这些代码放在 {} , 并包含显式的 return 语句 例如

        即使 lambda 表达式没有参数 仍然要提供空括号 就像无参数方法一样

        如果可以推导出一个 lambda 表达式的参数类型 则可以忽略其类型 例如

        在这里, 编译器可以推导出 first second 必然是字符串 因为这个 lambda 表达式将赋给一个字符串比较器。
        如果方法只有一个参数 而且这个参数的类型可以推导得出 那么甚至还可以省略小括号

        无需指定 lambda 表达式的返回类型 lambda 表达式的返回类型总是会由上下文推导得出。 例如 下面的表达式:

        可以在需要 int  类型结果的上下文中使用
        如果一个 lambda 表达式只在某些分支返回一个值 而在另外一些分支不返回值 , 这是不合法的。 例如 int x ) - > { if ( x > = 0 ) return 1 ; } 就不合法

        代码示例:

package FunctionProm;

import javax.swing.*;
import java.util.Arrays;
import java.util.Date;

public class LambdaTest {
    public static void main(String[] args) {
        String[] planets = new String[] { "Mercury" , "Venus" , "Earth" , "Mars" , "Jupiter" , "Saturn" , "Uranus" , "Neptune" };
        System.out.println(Arrays.toString(planets));
        System.out. println("Sorted in dictionary order:") ;
        Arrays.sort(planets);
        System.out.println (Arrays.toString(planets));
        System.out . println ("Sorted by length:");
        Arrays.sort(planets, (first, second) -> first.length() - second.length()) ;
        System.out. println(Arrays.toString(planets));
        Timer t = new Timer(1000, event ->
                System.out.println ("The time is " + new Date()));
        t.start();
        // keep program running until user selects "0k"
        JOptionPane.showMessageDialog (null , "Quit program?");
        System.exit(0);
    }
}

三、函数式接口

        前 面 已 经 讨 论 过, Java 中 已 经 有 很 多 封 装 代 码 块 的 接 口 ActionListener 或 Comparatorlambda 表达式与这些接口是兼容的。
        对于只有一个抽象方法的接口, 需要这种接口的对象时 就可以提供一个 lambda 表达式。 这种接口称为函数式接口 functional interface )
        为了展示如何转换为函数式接口, 下面考虑 Arrays . sort 方法 它的第二个参数需要一个 Comparator 实例 Comparator 就是只有一个方法的接口 所以可以提供一个 lambda 表达式

        在底层, Arrays . sort 方法会接收实现了 Comparator < String > 的某个类的对象 。 在这个对象上调用 compare 方法会执行这个 lambda 表达式的体。这些对象和类的管理完全取决于具体实现, 与使用传统的内联类相比,这样可能要高效得多。最好把 lambda 表达式看作是一个函数,而不是一个对象, 另外要接受 lambda 表达式可以传递到函数式接口。
        lambda 表达式可以转换为接口 ,这一点让 lambda 表达式很有吸引力 具体的语法很简短。 下面再来看一个例子

        与使用实现了 ActionListener 接口的类相比 这个代码可读性要好得多
        实际上, 在 Java 中, lambda 表达式所能做的也只是能转换为函数式接口 在其他支 持函数字面量的程序设计语言中, 可以声明函数类型 String , String ) - > int ) 声明这些类 型的变量, 还可以使用变量保存函数表达式 不过 Java 设计者还是决定保持我们熟悉的接口概念, 没有为Java 语言增加函数类型
        Java API java . util . fimction 包中定义了很多非常通用的函数式接口 其中一个接口BiFunction< T , U , R > 描述了参数类型为 T U 而且返回类型为 R 的函数 可以把我们的字符串比较 lambda 表达式保存在这个类型的变量中

        不过, 这对于排序并没有帮助 没有哪个 Arrays . sort 方法想要接收一个 BiFunction 如果你之前用过某种函数式程序设计语言, 可能会发现这很奇怪 不过 对于 Java 程序员而言, 这非常自然 类似 Comparator 的接口往往有一个特定的用途 而不只是提供一个有指定参数和返回类型的方法。 Java SE 8 沿袭了这种思路 想要用 lambda 表达式做某些处理 还是要谨记表达式的用途, 为它建立一个特定的函数式接口
        java . util . function 包中有一个尤其有用的接口 Predicate :

        ArrayList 类有一个 removelf 方法 它的参数就是一个 Predicate 这个接口专门用来传递 lambda 表达式 例如 下面的语句将从一个数组列表删除所有 null
        list. removelf ( e - > e = = null ) ;

        四、方法引用

        有时, 可能已经有现成的方法可以完成你想要传递到其他代码的某个动作 例如 假设你希望只要出现一个定时器事件就打印这个事件对象。 当然 为此也可以调用 :

        但是, 如果直接把 println 方法传递到 Timer 构造器就更好了 具体做法如下

        表达式 System . out :: println 是一个方法引用 method reference ) , 它等价于 lambda 表达式 x 一 > System . out . println ( x )
        再来看一个例子, 假设你想对字符串排序 而不考虑字母的大小写 可以传递以下方法表达式:

        从这些例子可以看出, 要用:: 操作符分隔方法名与对象或类名 主要有 3 种情况

        在前 2 种情况中 方法引用等价于提供方法参数的 lambda 表达式 前面已经提到 , System. out :: println 等价于 x - > System . out . println ( x)。 类似地, Math : : pow 等价于 x y ) - >
Math . pow ( x , y)。
        对于第 3 种情况 第 1 个参数会成为方法的目标 例如 String : : compareToIgnoreCase
同于 ( x , y ) - > x . compareToIgnoreCase ( y ) 。
        如果有多个同名的重栽方法, 编译器就会尝试从上下文中找出你指的那一个方法 。 例如, Math . max 方法有两个版本 一个用于整数 另一个用于 double 选择哪一个版 本取决于 Math :: max 转换为哪个函数式接口的方法参数 类似于 lambda 表达式 方法引用不能独立存在, 总是会转换为 函数式接口 的实例
        
        可以在方法引用中使用 this 参数 例如 this :: equals 等同于 x - > this . equals ( x ) 使用
super 也是合法的 下面的方法表达式:
        super: : instanceMethod
        使用 this 作为目标 会调用给定方法的超类版本
         为了展示这一点 下面给出一个假想的例子

        TimedGreeter. greet 方法开始执行时 会构造一个 Timer , 它会在每次定时器滴答时执行 super:: greet 方法 这个方法会调用超类的 greet 方法

        五、构造器引用

        构造器引用与方法引用很类似, 只不过方法名为 new 例如 Person : : new Person 构造 器的一个引用。 哪一个构造器呢 这取决于上下文 假设你有一个字符串列表 可以把它转换为一个 Person 对象数组 为此要在各个字符串上调用构造器 调用如下

        map 方法会为各个列表元素调用 Person ( String ) 构造器 如果有多个 Person 构造器 编译器会选择有一个 String 参数的构造器 因为它从上下文推导出这是在对一个字符串调用构造器。
        可以用数组类型建立构造器引用。 例如 int [] :: new 是一个构造器引用 它有一个参数 :即数组的长度。 这等价于 lambda 表达式 x - > new int [ x ]。
        Java 有一个限制 无法构造泛型类型 T 的数组 数组构造器引用对于克服这个限制很有用。 表达式 new T [ n ] 会产生错误 因为这会改为 new Object [ n]。
        对于开发类库的人来说, 这是一个问题。 例如 假设我们需要一个 Person 对象数组。
        Stream 接口有一个 toArray 方法可以返回 Object 数组

        不过, 这并不让人满意 用户希望得到一个 Person 引用数组 而不是 Object 引用数组 。流库利用构造器引用解决了这个问题。 可以把 Person [ ] : : new 传入 toArray 方法

        toArray方法调用这个构造器来得到一个正确类型的数组 然后填充这个数组并返回

        六、变量作用域

        通常, 你可能希望能够在 lambda 表达式中访问外围方法或类中的变量 考虑下面这个例子:
public static void repeatMessage(String text, int delay)
{
    ActionListener listener = event ->
        {
                System.out.println(text);
                Toolkit.getDefaultToolkit().beep():
    };
    new Timer(delay, listener).start();
}

        来看这样一个调用

        现在来看 lambda 表达式中的变量 text 注意这个变量并不是在这个 lambda 表达式中定义的。 实际上 这是 repeatMessage 方法的一个参数变量
        如果再想想看, 这里好像会有问题 尽管不那么明显 lambda 表达式的代码可能会在 repeatMessage 调用返回很久以后才运行 而那时这个参数变量已经不存在了 如何保留 text
变量呢
        要了解到底会发生什么, 下面来巩固我们对 lambda 表达式的理解 lambda 表达式有 3 个部分:
        1 ) 一个代码块
        2 ) 参数 ;
        3 ) 自由变量的值 这是指非参数而且不在代码中定义的变量
        在我们的例子中, 这个 lambda 表达式有 1 个自由变量 text 表示 lambda 表达式的数据结构必须存储自由变量的值, 在这里就是字符串 " Hello " 我们说它被 lambda 表达式捕获(下面来看具体的实现细节 例如 可以把一个 lambda 表达式转换为包含一个方法的对象, 这样自由变量的值就会复制到这个对象的实例变量中

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

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

相关文章

Window系统部署Splunk Enterprise并结合内网穿透实现远程访问本地服务

文章目录 前言1. 搭建Splunk Enterprise2. windows 安装 cpolar3. 创建Splunk Enterprise公网访问地址4. 远程访问Splunk Enterprise服务5. 固定远程地址 前言 本文主要介绍如何简单几步&#xff0c;结合cpolar内网穿透工具实现随时随地在任意浏览器&#xff0c;远程访问在本地…

Linux进程间通信4——消息队列

目录 1.原理 2.消息队列的系统调用 2.1 msgget 2.2 msgsnd 2.3 msgrcv 2.4 msgctl 3.消息队列的使用——代码演示 4.结论 1.原理 2.消息队列的系统调用 2.1 msgget 用于创建或者获取一个消息队列。成功返回消息队列ID&#xff0c;失败返回-1。 int msgget(key_t key,…

谷歌seo推广效果怎么判断?

要想判断谷歌SEO推广效果&#xff0c;核心在于观察和分析几个关键指标&#xff0c;一个网站最重要的自然就是流量&#xff0c;没有流量的网站说到底就是一个被雪藏的花架子&#xff0c;没什么意义&#xff0c;所以看流量自然就是最重要的指标&#xff0c;SEO做得好&#xff0c;…

【力扣白嫖日记】626.换座位

前言 练习sql语句&#xff0c;所有题目来自于力扣&#xff08;https://leetcode.cn/problemset/database/&#xff09;的免费数据库练习题。 今日题目&#xff1a; 626.换座位 表&#xff1a;Seat 列名类型idintstudentvarchar id 是该表的主键&#xff08;唯一值&#xf…

深入 Starknet 去中心化世界,探秘实用开发利器

Starknet 近期开放空投&#xff0c;面向 130 万地址总量发放超 7 亿枚 Token&#xff0c;让 ECMP 早期贡献者、GitHub 开源开发者、Starknet 用户等各个层面的生态参与者都得以深度参与。 盛宴的背后&#xff0c;是 Starknet 正迎来发展的关键机遇。在今年以太坊坎昆升级的背景…

绕过付费,畅享网络:自由浏览付费内容 | 开源日报 No.185

iamadamdev/bypass-paywalls-chrome Stars: 38.8k License: NOASSERTION bypass-paywalls-chrome 是一个用于 Chrome 和 Firefox 的网页浏览器扩展&#xff0c;可帮助绕过特定网站的付费墙。 可以绕过多个指定网站的付费墙支持自动更新&#xff08;仅限 Firefox 版本&#x…

10.轮廓系数-机器学习模型性能的常用的评估指标

轮廓系数&#xff08;Silhouette Coefficient&#xff09;是评估聚类算法效果的常用指标之一。它结合了聚类的凝聚度&#xff08;Cohesion&#xff09;和分离度&#xff08;Separation&#xff09;&#xff0c;能够量化聚类结果的紧密度和分离度。 背景 1.聚类分析的背景 在…

武汉灰京文化:游戏推广的领军者

在当今飞速发展的游戏行业中&#xff0c;游戏推广成为了每个游戏开发商和发行商必然要面对的挑战。如何能够将游戏信息传播给更广泛的受众群体&#xff0c;提升游戏的知名度和用户参与度&#xff0c;成为了每个游戏从业者需要思考的问题。而武汉灰京文化作为游戏推广领域的领军…

指定新加坡|高职老师自费赴新加坡国立大学访学交流

K老师任职于某高职院校&#xff0c;希望通过自费出国访学&#xff0c;达到拓宽国际化视野&#xff0c;为本校的专业发展寻求新契机的目的&#xff0c;并将访学目标国家指定为新加坡。最终我们为其获得新加坡国立大学的邀请函。因交叉性、前沿性的专业特性&#xff0c;K老师的出…

STM32CubeIDE基础学习-软件安装,环境搭建

STM32CubeIDE基础学习-软件介绍及环境搭建步骤 文章目录 STM32CubeIDE基础学习-软件介绍及环境搭建步骤前言第1章 STM32CubeIDE 介绍1.1 软件描述1.2 软件支持的功能及特点 第2章 STM32CubeIDE 软件安装2.1 STM32CubeIDE 软件获取方法2.2 STM32CubeIDE 软件安装步骤2.2.1 错误安…

一些C语言知识

C语言的内置类型&#xff1a; char short int long float double C99中引入了bool类型&#xff0c;用来表示真假的变量类型&#xff0c;包含true&#xff0c;false。 这个代码的执行结果是什么&#xff1f;好好想想哦&#xff0c;坑挺多的。 #include <stdio.h>int mai…

《精益DevOps》:填补IT服务交付的认知差距,实现高效可靠的客户期望满足

写在前面 在当今的商业环境中&#xff0c;IT服务交付已经成为企业成功的关键因素之一。然而&#xff0c;实现高效、可靠、安全且符合客户期望的IT服务交付却是一项艰巨的任务。这要求服务提供商不仅具备先进的技术能力&#xff0c;还需要拥有出色的组织协作、流程管理和态势感…

(2024,随机交叉注意力,冻结预训练模型的 K 和 V)StochCA:一种利用交叉注意力开发预训练模型的新方法

StochCA: A Novel Approach for Exploiting Pretrained Models with Cross-Attention 公和众和号&#xff1a;EDPJ&#xff08;进 Q 交流群&#xff1a;922230617 或加 VX&#xff1a;CV_EDPJ 进 V 交流群&#xff09; 目录 0. 摘要 3. 方法 3.1 问题陈述 3.1.1 迁移学习 …

LeetCode206题:反转链表(python3)

采用递归 class Solution:def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:cur headpre Nonewhile cur:temp cur.next # 保存下一轮循环的节点cur.next pre # 将当前节点 cur 的指针指向上一个节点 prepre curcur tempreturn pre

Network LSA 结构简述

Network LSA主要用于描述一个区域内的网络拓扑结构&#xff0c;包括网络中的路由器和连接到这些路由器的网络。它记录了每个路由器的邻居关系、连接状态以及连接的度量值&#xff08;如带宽、延迟等&#xff09;&#xff0c;以便计算最短路径和构建路由表。display ospf lsdb n…

08. Nginx进阶-Nginx动静分离

简介 什么是动静分离&#xff1f; 通过中间件将动态请求和静态请求进行分离。分离资源&#xff0c;减少不必要的请求消耗&#xff0c;减少请求延时。 动静分离的好处 动静分离以后&#xff0c;即使动态服务不可用&#xff0c;静态资源仍不受影响。 动静分离示意图 动静分离…

抉择IT工单管理系统:SaaS云部署VS本地部署,亿发解读优势与成本

在选择IT运维工单系统时&#xff0c;企业经常需要考虑部署在公有云&#xff08;SaaS&#xff09;还是本地部署。实际上&#xff0c;两种部署方法都有各自的优势&#xff0c;企业应该综合考虑自身的资金成本、安全性需求和便捷性等因素。那么SaaS云部署和本地部署有什么区别呢&a…

ST32/GD32——UART串口通信

芯片选型 Ciga Device — GD32F470系列 串口通信 串口通信指的是通过串行通信接口进行数据传输的通信方式&#xff0c;通常用于短距离、低速率的数据传输。 串口通信可以使用不同的串行通信协议和接口&#xff0c;常见的串口通信协议有UART、USART、RS-232、RS-485、SPI、I…

为什么阿里不推荐使用 keySet() 遍历HashMap?

引言 HashMap相信所有学Java的都一定不会感到陌生,作为一个非常重用且非常实用的Java提供的容器,它在我们的代码里面随处可见。因此遍历操作也是我们经常会使用到的。HashMap的遍历方式现如今有非常多种: 使用迭代器(Iterator)。 使用 keySet() 获取键的集合,然后通过增强…

【数仓】Hadoop软件安装及使用(集群配置)

一、环境准备 1、准备3台虚拟机 Hadoop131&#xff1a;192.168.56.131Hadoop132&#xff1a;192.168.56.132Hadoop133&#xff1a;192.168.56.133 本例系统版本 CentOS-7.8&#xff0c;已安装jdk1.8 2、hosts配置&#xff0c;关闭防火墙 vi /etc/hosts添加如下内容&#x…