一个同事的问题,下面的代码运行时引用的外部变量TestClosure.count
没有初始化:
object TestClosure extends App {
val words = Array("a","ab","abc")
val count = 10
val cnt = words.map{word => (word, count)}
cnt.foreach(println)
}
object TestRef extends App {
//对应上面map里面那个匿名函数
val c = Class.forName("TestClosure$$anonfun$1")
val meod = c.getDeclaredMethod("apply", classOf[String])
val res = meod.invoke(c.newInstance(), "zhang")
// (zhang,0) 并不是 (zhang,10),说明外层object的count并没有被赋值
println(res)
}
如果运行 TestClosure
是ok的:
$ scala TestClosure
(a,10)
(ab,10)
(abc,10)
但是运行 TestRef
时,发现引用的TestClosure
里的count
变量没有被赋值:
$ scala TestRef
(zhang,0)
这个问题咋一看以为是闭包上下文绑定问题,实际上与闭包无关,是因为继承了App
特质导致的,看一下App
特质:
trait App extends DelayedInit
DelayedInit
特质里定义了延迟初始化方法:
def delayedInit(x: => Unit): Unit
scala在运行时同java,由一个包含main方法的单例作为入口,大概是2.8的时候为了简便,设计了App
特质,由App
提供main
方法,用户可以直接在初始化块里写逻辑,然后编译器会把这段初始化代码块里的逻辑封装成一个函数对象缓存起来(并没有运行),只有在执行到main
方法的时候才会触发。
通过一个简单的例子看一下,首先看没有继承App
特质的情况:
object Foo {
val count = 10
println(count)
}
上面的代码,在翻译为java时,成员的赋值,以及println
都是在构造函数或构造块里执行的
class Foo$ {
private final int count;
private Foo$(){
count = 10;
println(count);
}
// 忽略getter等其他不相关内容
}
再看看 Foo
继承自 App
之后:
object Foo extends App {
val count = 10
println(count)
}
翻译成java代码时,构造函数里相当于:
class Foo$ implements App {
private final int count;
private Foo$(){
// 逻辑被封装起来,延迟到main方法时才执行
delayedInit( anonymousFunction{count = 10; println(count)});
}
}
逻辑并没有被执行,而是封装在initCode
这个Buffer
里:
/** The init hook. This saves all initialization code for execution within `main`.
* This method is normally never called directly from user code.
* Instead it is called as compiler-generated code for those classes and objects
* (but not traits) that inherit from the `DelayedInit` trait and that do not
* themselves define a `delayedInit` method.
* @param body the initialization code to be stored for later execution
*/
override def delayedInit(body: => Unit) {
initCode += (() => body)
}
只有main
方法执行时,才会触发这些逻辑,见App.main
:
def main(args: Array[String]) = {
...
for (proc <- initCode) proc()
...
}
所以原因就在TestClosure
这个单例继承了App
导致内部的逻辑延迟初始化,取消继承App
就正常了。
相关推荐
移动端app项目代码初始化
8.3_详情页面数据初始化|uni-app_项目实战(详情页功能模块)|uni-app_&_uniCloud_从零入门开发《IT
如果学习Android加固与脱壳不学习这些基础的话,那么后面加固点和脱壳点能让你轻轻松松看得云里雾里,不知所以。想学这一门手艺,该跨的门槛一点儿马虎不得,但是我有预感,当你把这些基础底层难啃的知识整出些门道...
Android推送遇到的问题--app关闭后,点击通知后点击通知后app未初始化的问题
Android.-Camera-app初始化分析.doc
AudioPolicy&AudioFlinger初始化总体框架初始化步骤简介初始化步骤详细流程分析1、loadConfig()2、initialize()2.1、初始音频路由引擎2.2、加载so 并且打开设备节点2.3、打开输出流 总体框架 AudioFlinger和Audio...
用于app模块初始化,可区分进程,线程,并设置优先级
使用CRA(create-react-app)初始化一个完整的项目环境(该初始化项目已上传到本文章的资源)
uin-app仿瑞幸咖啡app5个初始页面
App结构的vue项目初始化搭建
5.13_内容卡片数据初始化|uni-app_项目实战(首页功能模块)|uni-app_&_uniCloud_从零入门开发《IT
其锚点提供 "勾住" 依赖的功能,能灵活解决初始化过程中复杂的同步问题。参考 alpha 并改进其部分细节, 更贴合 Android 启动的场景, 同时支持优化依赖初始化流程, 自动选择较优的路径进行初始化。
5.6_选项卡数据初始化|uni-app_项目实战(首页功能模块)|uni-app_&_uniCloud_从零入门开发《IT技术
当android系统启动WIFI 模块时, 它将做四件事:
5.1_项目初始化|uni-app_项目实战(首页功能模块)|uni-app_&_uniCloud_从零入门开发《IT技术资讯类
简单的实现第一次打开APP的时候初始化页面的效果,对于新手来说是个不错的学习demo
如题,这组文档详尽的描述分析了oracle初始化的过程及其参数的含义及设置的方式,对于要深入了解oracle的朋友是很有帮助的。
以下是对应的头文件和源文件,初始化顺序分为9级,LOADER_BOARD_EARLY_EXPORT最早,LOADER_APP_LATE_EXPORT最迟。使用方法可以看最后面的Demo。 #ifndef _LOADER__H #define _LOADER__H #if defined(__CC_ARM) || ...
烧瓶端口,用于初始化下一个应用程序的项目结构。 -如何创建一个新的应用程序。 Create Flask应用程序可在macOS,Windows和Linux上运行。 如果无法解决,请。 如果您有任何疑问,建议或需要帮助,请。 快速概述 ...
而是在 Splash 之后 延迟做初始化。 这样就存在了性能问题,因为某些组件可能一直并没有使用到,但却被初始化了。 > 之前的组件化框架,是直接通过一个 ServiceManager 来管理的,这也导致 ServiceManager 非常重。 ...