当前位置: 首页 > news >正文

Scala关键字lazy的见解

Scala中使用关键字lazy来定义惰性变量,实现延迟加载(懒加载)。
惰性变量只能是不可变变量,并且只有在调用惰性变量时,才会去实例化这个变量。

在Java中,要实现延迟加载(懒加载),需要自己手动实现。一般的做法是这样的:


public class LazyDemo {private String property;public String getProperty() {if (property == null) {//如果没有初始化过,那么进行初始化property = initProperty();}return property;
}private String initProperty() {return "property";}
}

比如常用的单例模式懒汉式实现时就使用了上面类似的思路实现。

而在Scala中对延迟加载这一特性提供了语法级别的支持:

lazy val property = initProperty()

使用lazy关键字修饰变量后,只有在使用该变量时,才会调用其实例化方法。也就是说在定义property=initProperty()时并不会调用initProperty()方法,只有在后面的代码中使用变量property时才会调用initProperty()方法。

如果不使用lazy关键字对变量修饰,那么变量property是立即实例化的:


object LazyOps {def init(): String = {println("call init()")return ""}def main(args: Array[String]) {val property = init();//没有使用lazy修饰println("after init()")println(property)}}

上面的property没有使用lazy关键字进行修饰,所以property是立即实例化的,如果观察程序的输出:

call init()
after init()

可以发现,property声明时,立即进行实例化,调用了`init()``实例化方法

而如果使用lazy关键字进行修饰:

object LazyOps {def init(): String = {println("call init()")return ""}def main(args: Array[String]) {lazy val property = init();//使用lazy修饰println("after init()")println(property)println(property)}}

观察输出:

after init()
call init()

在声明property时,并没有立即调用实例化方法intit(),而是在使用property时,才会调用实例化方法,并且无论缩少次调用,实例化方法只会执行一次。

与Java相比起来,实现懒加载确实比较方便了。那么Scala是如何实现这个语法糖的呢?反编译看下Scala生成的class:

private final String property$lzycompute$1(ObjectRef property$lzy$1, VolatileByteRef bitmap$0$1){synchronized (this)//加锁{if ((byte)(bitmap$0$1.elem & 0x1) == 0)//如果属性不为null{//那么进行初始化property$lzy$1.elem = init();bitmap$0$1.elem = ((byte)(bitmap$0$1.elem | 0x1));}return (String)property$lzy$1.elem;}}

原理探究
scala也是编译成字节码跑在jvm上的,而jvm的字节码指令并没有提供对lazy这种语义的支持,所以由此可以推断,lazy只是一个语法糖,scala编译器在编译时期对其做一些包装转换,但究竟是如何转换的呢,可以写一段代码编译然后反编译看一下。

编写一段scala代码,有两个变量,一个使用lazy修饰,一个不使用lazy修饰:

package cc11001100.scala.lazyStudyclass LazyInitDemoForDecompilation {lazy val foo = "foo"val bar = "bar"
}object LazyInitDemoForDecompilation {def main(args: Array[String]): Unit = {val o = new LazyInitDemoForDecompilation()println(o.foo)println(o.bar)}}

然后编译为字节码文件,再使用jd-gui等工具将其反编译:

package cc11001100.scala.lazyStudy;import scala.reflect.ScalaSignature;@ScalaSignature(bytes="\006\001}2A!\003\006\001#!)q\003\001C\0011!A1\004\001EC\002\023\005A\004C\004&\001\t\007I\021\001\017\t\r\031\002\001\025!\003\036\017\0259#\002#\001)\r\025I!\002#\001*\021\0259b\001\"\001+\021\025Yc\001\"\001-\005qa\025M_=J]&$H)Z7p\r>\024H)Z2p[BLG.\031;j_:T!a\003\007\002\0231\f'0_*uk\022L(BA\007\017\003\025\0318-\0317b\025\005y\021AC2dcE\002\004'M\0311a\r\0011C\001\001\023!\t\031R#D\001\025\025\005i\021B\001\f\025\005\031\te.\037*fM\0061A(\0338jiz\"\022!\007\t\0035\001i\021AC\001\004M>|W#A\017\021\005y\031S\"A\020\013\005\001\n\023\001\0027b]\036T\021AI\001\005U\0064\030-\003\002%?\t11\013\036:j]\036\f1AY1s\003\021\021\027M\035\021\00291\013'0_%oSR$U-\\8G_J$UmY8na&d\027\r^5p]B\021!DB\n\003\rI!\022\001K\001\005[\006Lg\016\006\002.aA\0211CL\005\003_Q\021A!\0268ji\")\021\007\003a\001e\005!\021M]4t!\r\0312'N\005\003iQ\021Q!\021:sCf\004\"AN\037\017\005]Z\004C\001\035\025\033\005I$B\001\036\021\003\031a$o\\8u}%\021A\bF\001\007!J,G-\0324\n\005\021r$B\001\037\025\001")
public class LazyInitDemoForDecompilation
{private String foo;private String foo$lzycompute(){// 因为在调用此方法之前已经判断过一次标志位的值了,// 所以可以看做是一种被拆散了的DCLsynchronized (this){if (!this.bitmap$0){this.foo = "foo";this.bitmap$0 = true;}}return this.foo;}public String foo(){// 每次获取foo的值的时候,先判断是否已经初始化过了,// 如果还没有初始化就将其初始化,否则直接将已经计算出的值返回return !this.bitmap$0 ? foo$lzycompute() : this.foo;}public String bar(){return this.bar;}// bar变量直接为其赋值的private final String bar = "bar";// 这个变量是一个标志位,用来记录foo变量是否已经被初始化过了private volatile boolean bitmap$0;public static void main(String[] paramArrayOfString){LazyInitDemoForDecompilation..MODULE$.main(paramArrayOfString);}
}

在object中执行:

package cc11001100.scala.lazyStudy;import scala.Predef.;public final class LazyInitDemoForDecompilation$
{public static  MODULE$;static{new ();}public void main(String[] args){LazyInitDemoForDecompilation o = new LazyInitDemoForDecompilation();// 会将对变量的访问替换成调用访问器,// 这样的话编译器就可以很鸡贼的在访问器方法中插入各种处理以提供N多的语法糖,挺机智的Predef..MODULE$.println(o.foo());Predef..MODULE$.println(o.bar());}private LazyInitDemoForDecompilation$(){MODULE$ = this;}
}

综上源码,得出结论,scala的lazy关键字就是编译器在编译期将变量的初始化过程替换为Double Check Lock,类似于Java中的懒汉式单例模式初始化。

Scala同样使用了Java中常用的懒加载的方式自动帮助我们实现了延迟加载,并且还加锁避免多个线程同时调用初始化方法可能导致的不一致问题。

对于这样一个表达式: lazy val t:T = expr 无论expr是什么东西,字面量也好,方法调用也好。Scala的编译器都会把这个expr包在一个方法中,并且生成一个flag来决定只在t第一次被访问时才调用该方法。

相关文章:

Scala关键字lazy的见解

Scala中使用关键字lazy来定义惰性变量,实现延迟加载(懒加载)。 惰性变量只能是不可变变量,并且只有在调用惰性变量时,才会去实例化这个变量。 在Java中,要实现延迟加载(懒加载),需要自己手动实现。一般的做法是这样的…...

sql分类 DDL、DML、DCL

DDL (Data Definition Language 数据定义语言) 这些语句定了不同的数据库、表、视图、索引等数据库对象,还可以用来创建、删除、修改数据库和数据表的结构 如: CREATE \ DROP \ ALTER \ RENAME \ TRUNCATE 等 DML(Data Manipulation Langua…...

C++ 性能优化

要系统地提升C项目的性能,可以采取以下步骤: 分析和度量:首先,你需要通过性能分析工具来确定项目中的性能瓶颈。使用工具如gprof、perf等,来识别代码中消耗时间和资源最多的部分。 选择合适的数据结构和算法&#xff…...

435. 无重叠区间

435. 无重叠区间 给定一个区间的集合 intervals ,其中 intervals[i] [starti, endi] 。返回 需要移除区间的最小数量,使剩余区间互不重叠 。 示例 1: 输入: intervals [[1,2],[2,3],[3,4],[1,3]] 输出: 1 解释: 移除 [1,3] 后,剩下的区间…...

winform使用SetParent 嵌入excel,打开的excel跟随dpi 25%*125%缩放了两次,目前微软官方没有好的解决方案,为什么

双重缩放问题在将 Excel 嵌入到 WinForm 中时确实可能会出现,这是因为两个不同的应用程序(WinForm 和 Excel)之间的 DPI 缩放逻辑不一致,导致双重缩放的结果。 在 Windows 操作系统中,DPI 缩放是一种全局的设置&#…...

MySQL 数据库、表的基本操作

目录 数据库 关系数据库SQL 关系数据库常用词汇 常用命令语句 数据库操作 查看数据库 创建数据库 修改数据库编码 删除数据库 数据表操作 查看数据表 创建数据表 表中数据操作 增 删 改 查 数据库 数据库是在数据管理和程序开发过程中,一种非常重要…...

html5播放器视频切换和连续播放的实例

当前播放器实例可以使用changeVid接口切换正在播放的视频。当有多个视频,在上一个视频播放完毕时,自动播放下一个视频时也可采用该处理方式。 const option {vid: 88083abbf5bcf1356e05d39666be527a_8,//autoplay: true,//playsafe: , //PC端播放加密视…...

什么是无服务器架构技术

什么是无服务器架构技术 无服务器架构(Serverless Architecture)是jin年来逐渐兴起的一种软件架构方案,它采用了一种全新的方式来处理应用程序的部署、运行和扩展。与传统的服务器架构相比,无服务器架构具有很多优势,包…...

大数据开发的学习路线是什么样的

大数据技术的体系庞大且复杂,每年都会涌现出大量新的技术,目前大数据行业所涉及到的核心技术主要就是:数据采集、数据存储、数据清洗、数据查询分析和数据可视化。 学习大数据需要掌握什么语言基础? 1、Java基础 大数据框架90%以…...

深入解析Spring MVC注解:@PathVariable、@ResponseBody和@RequestParam的用法和区别

简介 在Spring MVC框架中,PathVariable、ResponseBody和RequestParam是常用的注解,它们分别用于处理请求的路径变量、响应数据格式和请求参数。本文将深入介绍这些注解的用法,并详细讨论它们之间的区别,以便开发者在构建Web应用程…...

自然语言处理学习笔记(一)————概论

目录 1.自然语言处理概念 2.自然语言与编程语言的比较 (1)词汇量: (2)结构化: (3)歧义性: (4)容错性: (5&#xff0…...

C# wpf程序

--App.xaml namespace WpfMyproject { /// <summary> /// App.xaml 的交互逻辑 /// </summary> public partial class App : PrismApplication { protected override Window CreateShell() { return Container.R…...

4G WWAN设备类型

WWAN设备类型 USB dongle是设备接入互联网的重要方式之一&#xff0c;典型的通过USB接口与主设备连接&#xff0c;然后主设备通过4G/5G接入互联网&#xff0c;作为移动宽带设备&#xff0c;它有那些设备类型及暴露方式呢&#xff1f; 移动宽带设备类型&#xff1a;ModemManage…...

windows环境下安装elasticsearch、kibana

通过本文可以快速在windows系统上安装elasticsearch、kibana环境。 当你用Integer类型的时候&#xff0c;要非常小心&#xff0c;因为100等于100、但是200不等于200&#xff0c;当然&#xff0c;如果你会一点小花招&#xff0c;也可以让100不等于100、让200等于200。(运算符比较…...

Java Selenium WebDriver 网页填报

一、windows环境安装配置 1.安装chrome浏览器 在“关于chrome”界面&#xff0c;查看浏览器版本号 2.下载chromeDriver 在https://registry.npmmirror.com/binary.html?pathchromedriver/下载对应版本的驱动&#xff08;如果浏览器版本过新&#xff0c;建议下载最接近的版…...

【NLP概念源和流】 06-编码器-解码器模型(6/20 部分)

一、说明 在机器翻译等任务中,我们必须从一系列输入词映射到一系列输出词。读者必须注意,这与“序列标记”不同,在“序列标记”中,该任务是将序列中的每个单词映射到预定义的类,如词性或命名实体任务。 作者生成 在上面的...

运维必备的免费在线画图工具,你觉得哪个最好用呢

都说一图胜千言&#xff0c;一个IT工程师如果能画的一手好图&#xff0c;无论是在方案选项、还是技术交流&#xff0c;都能快速表达自己的想法&#xff0c;让你的思路更加的直观明了&#xff1b;市面上的制图工具有很多&#xff0c;下面就推荐几款好用且免费的工具&#xff0c;…...

skywalking全链路追踪

文章目录 一、介绍二、全链路追踪1. 测试1 - 正常请求2. 测试2 - 异常请求 三、过滤非业务请求链路1. 链路忽略插件2. 配置3. 测试 一、介绍 在上一篇文章skywalking安装教程中我们介绍了skywalking的作用以及如何将其集成到我们的微服务项目中。本篇文章我们介绍在微服务架构…...

Nacos配置中心设置Mongodb

目录 1.common模块导入nacos config依赖 2.common模块新建bootstrap.yaml 3.在自己的模块导入common模块依赖 4.打开nacos新建配置&#xff0c;发布 5.运行服务并测试 效果&#xff1a;在部署完成后&#xff0c;其他人可以自动连接到你本地mongoDB数据库&#xff0c;无需再…...

【Spring Boot】请求参数传json对象,后端采用(map)CRUD案例(101)

请求参数传json对象&#xff0c;后端采用&#xff08;map&#xff09;接收的前提条件&#xff1a; 1.Spring Boot 的Controller接受参数采用&#xff1a;RequestBody 2.需要一个Json工具类&#xff0c;将json数据转成Map&#xff1b; 工具类&#xff1a;Json转Map import com…...

微信小程序之bind和catch

这两个呢&#xff0c;都是绑定事件用的&#xff0c;具体使用有些小区别。 官方文档&#xff1a; 事件冒泡处理不同 bind&#xff1a;绑定的事件会向上冒泡&#xff0c;即触发当前组件的事件后&#xff0c;还会继续触发父组件的相同事件。例如&#xff0c;有一个子视图绑定了b…...

【WiFi帧结构】

文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成&#xff1a;MAC头部frame bodyFCS&#xff0c;其中MAC是固定格式的&#xff0c;frame body是可变长度。 MAC头部有frame control&#xff0c;duration&#xff0c;address1&#xff0c;address2&#xff0c;addre…...

Admin.Net中的消息通信SignalR解释

定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...

STM32F4基本定时器使用和原理详解

STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...

基于Uniapp开发HarmonyOS 5.0旅游应用技术实践

一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架&#xff0c;支持"一次开发&#xff0c;多端部署"&#xff0c;可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务&#xff0c;为旅游应用带来&#xf…...

五年级数学知识边界总结思考-下册

目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解&#xff1a;由来、作用与意义**一、知识点核心内容****二、知识点的由来&#xff1a;从生活实践到数学抽象****三、知识的作用&#xff1a;解决实际问题的工具****四、学习的意义&#xff1a;培养核心素养…...

Qt Http Server模块功能及架构

Qt Http Server 是 Qt 6.0 中引入的一个新模块&#xff0c;它提供了一个轻量级的 HTTP 服务器实现&#xff0c;主要用于构建基于 HTTP 的应用程序和服务。 功能介绍&#xff1a; 主要功能 HTTP服务器功能&#xff1a; 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...

Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!

一、引言 在数据驱动的背景下&#xff0c;知识图谱凭借其高效的信息组织能力&#xff0c;正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合&#xff0c;探讨知识图谱开发的实现细节&#xff0c;帮助读者掌握该技术栈在实际项目中的落地方法。 …...

零基础设计模式——行为型模式 - 责任链模式

第四部分&#xff1a;行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习&#xff01;行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想&#xff1a;使多个对象都有机会处…...

项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)

Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败&#xff0c;具体原因是客户端发送了密码认证请求&#xff0c;但Redis服务器未设置密码 1.为Redis设置密码&#xff08;匹配客户端配置&#xff09; 步骤&#xff1a; 1&#xff09;.修…...