一个同事的问题,下面的代码运行时引用的外部变量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完全关闭的状态下点击推送通知时,可能会遇到应用没有正确初始化的问题,导致通知的处理出现问题。这个问题涉及到Android的生命周期管理、服务、广播接收器以及通知的点击处理等多个方面。 首先,...
8.3_详情页面数据初始化|uni-app_项目实战(详情页功能模块)|uni-app_&_uniCloud_从零入门开发《IT
- 除了启动时的自动初始化,App Startup 也支持延迟初始化。可以使用 `AppInitializer` 类手动触发组件的初始化,适用于那些非启动阶段必须的组件。 - 若要关闭所有组件的自动初始化,可以从 AndroidManifest.xml ...
AudioPolicy&AudioFlinger初始化总体框架初始化步骤简介初始化步骤详细流程分析1、loadConfig()2、initialize()2.1、初始音频路由引擎2.2、加载so 并且打开设备节点2.3、打开输出流 总体框架 AudioFlinger和Audio...
如果学习Android加固与脱壳不学习这些基础的话,那么后面加固点和脱壳点能让你轻轻松松看得云里雾里,不知所以。想学这一门手艺,该跨的门槛一点儿马虎不得,但是我有预感,当你把这些基础底层难啃的知识整出些门道...
本项目“Android-用于app模块初始化可区分进程线程并设置优先级”聚焦于如何有效地管理进程和线程,以提高应用的性能和响应速度。下面将详细介绍这个项目中的核心知识点。 1. **进程与线程的概念** - **进程**:是...
Android.-Camera-app初始化分析.doc
10. **应用程序钩子函数初始化**:根据`OS_CFG_APP_HOOKS_EN`宏定义,初始化各个应用程序钩子函数指针。 11. **优先级初始化**:调用`OS_PrioInit()`函数,初始化优先级表。 12. **就绪任务列表初始化**:调用`OS_...
使用CRA(create-react-app)初始化一个完整的项目环境(该初始化项目已上传到本文章的资源)
在Android开发中,正确地初始化Qmui对于利用其功能至关重要。本压缩包"火山-qmui初始化方法.rar"可能包含了一份详细的教程或代码示例,用于指导开发者如何在项目中设置和启动Qmui框架。 Qmui初始化主要包括以下几个...
App结构的vue项目初始化搭建
5.13_内容卡片数据初始化|uni-app_项目实战(首页功能模块)|uni-app_&_uniCloud_从零入门开发《IT
uni-app 初始模板 简介 使用方式创建的 uni-app 项目 模板具有以下功能特性: 使用引入和UI 框架 使用作为网络请求库。并完善了拦截器、全局请求 loading 控制等 使用库让小程序和 APP 端支持 cookie 使用库让 vuex ...
5.6_选项卡数据初始化|uni-app_项目实战(首页功能模块)|uni-app_&_uniCloud_从零入门开发《IT技术
其锚点提供 "勾住" 依赖的功能,能灵活解决初始化过程中复杂的同步问题。参考 alpha 并改进其部分细节, 更贴合 Android 启动的场景, 同时支持优化依赖初始化流程, 自动选择较优的路径进行初始化。
ML307R OpenCPU 网络初始化流程介绍
5.1_项目初始化|uni-app_项目实战(首页功能模块)|uni-app_&_uniCloud_从零入门开发《IT技术资讯类
在解决app冷启动问题上,IntentService可以用来预先加载一些资源或执行初始化操作,比如预加载数据库、初始化网络连接等。 在解决冷启动白屏问题时,通常有以下策略: 1. **优化启动流程**:分析启动过程中的每个...
7. **使用 UiAutomator2 的自定义初始化脚本**:对于某些复杂情况,可以编写一个UiAutomator2的自定义初始化脚本来处理安装过程中的特定交互。 通过上述方法,我们可以有效地解决Appium在安装应用时需要手动确认的...
本文主要探讨的是社会化APP的首页设计,尤其是如何通过优化技术手段提升用户体验,其中延迟加载(Lazy Loading)是一项关键策略。延迟加载是一种网页或应用加载技术,它允许内容分批按需加载,而非一次性全部显示,...