涵盖:作用域函数(let / run / with / apply / also)、委托(by)、惰性初始化(lazy)、 以及 takeif、runcatching、use 等高频工具函数。
1.let
签名:inline fun <t, r> t.let(block: (t) -> r): r
对非空对象执行操作,并可改变返回类型。
1.1 空安全调用(最常用)
val name: string? = user?.nickname
// 传统写法
if (name != null) {
log.d(tag, "长度 = ${name.length}")
}
// let 写法:只在非空时执行
name?.let {
log.d(tag, "长度 = ${it.length}")
}
// 结合 elvis:为空时给兜底
val display = name?.let { "昵称:$it" } ?: "昵称:未设置"
1.2 给表达式起局部名字
// 避免写两次耗时函数
getusername()?.let { n ->
println("你好,$n")
cache.put("name", n)
}
1.3 链式转换(把 a 类型转成 b 类型)
val length: int? = listof("a", "bb", "ccc")
.firstornull { it.length > 1 }
.let { it?.length ?: 0 }
1.4 限制变量作用域
val result = calculatetotal().let { total ->
// total 只在这个块内可见,避免污染外部作用域
if (total > 1000) total * 0.9 else total
}
1.5 android:点击事件里避免重复 find
binding.btnsubmit.setonclicklistener {
viewmodel.submit()?.let { state ->
toast.maketext(this, state.message, toast.length_short).show()
}
}
2.run
签名
inline fun <r> run(block: () -> r): r // 非扩展:开一个新作用域 inline fun <t, r> t.run(block: t.() -> r): r // 扩展:接收者是 this
2.1t.run {}—— 判空 + 一串操作
val user: user? = repository.finduser(id)
val summary = user?.run {
// 这里 this 就是 user,属性可省略 this.
"$name($age 岁)"
} ?: "用户不存在"
2.2 非扩展run {}—— 需要一段"临时作用域"
val padding = run {
val base = resources.getdimensionpixelsize(r.dimen.base)
val half = base / 2
half * 3 // 最后一行就是 run 的返回值
}
2.3 android:一段有中间变量的计算
private fun buildquery(): map<string, string> = run {
val page = currentpage + 1
val size = pagesize.coerceatmost(50)
mapof("page" to "$page", "size" to "$size")
}
2.4 android:把一串 ui 操作收拢
fun showemptystate() {
binding.emptyview.run {
isvisible = true
text = getstring(r.string.empty_tip)
setonclicklistener { viewmodel.reload() }
}
}
3.with
签名:inline fun <t, r> with(receiver: t, block: t.() -> r): r
注意:with 不是扩展函数,它是普通函数,接收者作为第一个参数传入。
3.1 对同一对象做多次操作
val sb = stringbuilder()
with(sb) {
append("hello")
append(", ")
append("kotlin")
}
println(sb.tostring()) // hello, kotlin
3.2 android:一次性配置 recyclerview
with(binding.recyclerview) {
layoutmanager = linearlayoutmanager(context)
adapter = orderadapter
sethasfixedsize(true)
additemdecoration(divideritemdecoration(context, divideritemdecoration.vertical))
}
3.3 android:解析 bundle / 读取 intent 参数
val detail = with(requirearguments()) {
detailargs(
id = getstring("id") ?: "",
from = getstring("from") ?: "unknown",
editable = getboolean("editable", false)
)
}
3.4 什么时候用with而不是t.run?
// 两者等价,写法差异:
with(user) { println(name) } // with(user) { ... }
user.run { println(name) } // user.run { ... }
// 习惯:可空对象用 ?.run {},非空对象用 with(x) {} 更直观
4.apply
签名:inline fun <t> t.apply(block: t.() -> unit): t
返回对象本身,块内 this 是对象,适合"创建即配置"。
4.1 创建时批量设置属性
val intent = intent(context, detailactivity::class.java).apply {
putextra("id", orderid)
putextra("from", "list")
flags = intent.flag_activity_new_task
}
startactivity(intent)
4.2 android:构建 dialog / 配置 view
alertdialog.builder(this).apply {
settitle(r.string.tip)
setmessage(r.string.confirm_delete)
setpositivebutton(r.string.ok) { _, _ -> viewmodel.delete() }
setnegativebutton(r.string.cancel, null)
}.show()
4.3 初始化一个数据对象
val request = orderrequest().apply {
userid = uid
page = 1
pagesize = 20
sort = "createtime,desc"
}
4.4 链式:apply之后继续用对象
val text = textview(context).apply {
this.text = "标题" // 与属性名冲突时用 this 区分
textsize = 16f
settextcolor(color.black)
gravity = gravity.center
}.text.tostring()
apply 里如果省略 this.,要注意「参数名/属性名同名」的遮蔽问题。
5.also
签名:inline fun <t> t.also(block: (t) -> unit): t
返回对象本身,块内用 it,非常适合插入副作用而不打断链式调用。
5.1 打日志 / 埋点
val order = repository.createorder(req)
.also { log.d(tag, "创建订单成功: ${it.id}") }
.also { analytics.track("order_created", mapof("id" to it.id)) }
5.2 链式中的校验
val file = file(path)
.also { require(it.exists()) { "文件不存在: $path" } }
.also { require(it.canread()) { "文件不可读" } }
5.3 区分apply和also
// apply:配置对象内部,省略 this
val user = user().apply {
name = "tom" // 等价 this.name = "tom"
}
// also:对外部变量做副作用,必须用 it
val user = user().also {
it.name = "tom" // 必须写 it.name
loguser(it) // 副作用:记录日志
}
6.lazy惰性初始化
签名:fun <t> lazy(initializer: () -> t): lazy<t>
第一次访问才初始化,之后复用缓存值。
6.1 基础用法
val heavydata: list<string> by lazy {
log.d(tag, "只在首次访问时执行")
loadfromdb() // 耗时操作
}
// 第一次访问触发初始化
println(heavydata.size)
// 第二次访问直接拿缓存,不会再次执行 loadfromdb()
println(heavydata.size)
6.2 线程安全模式
// 默认:synchronized,加锁保证只初始化一次(多线程安全,略有开销)
val a: string by lazy { "a" }
// publication:允许多线程并发初始化,最终取第一次完成的值
val b: string by lazy(lazythreadsafetymode.publication) { "b" }
// none:无任何线程安全保证,最快;仅限单线程确定场景
val c: string by lazy(lazythreadsafetymode.none) { "c" }
6.3 android:by lazy三件套
class mainactivity : appcompatactivity() {
// 1. viewbinding
private val binding by lazy { activitymainbinding.inflate(layoutinflater) }
// 2. 只在用到时才创建的适配器
private val adapter by lazy {
orderadapter(onitemclick = { order -> opendetail(order.id) })
}
// 3. gson / retrofit 等单例工具
private val gson: gson by lazy { gsonbuilder().setlenient().create() }
}
7.by属性委托
kotlin 委托语法:val/var 属性 by 委托对象,属性的 get/set 交给委托对象实现。
7.1 标准委托一:lazy(延迟初始化)
见上一节。
7.2 标准委托二:observable(监听变化)
var score: int by delegates.observable(0) { prop, old, new ->
// 每次赋值后回调(无法拦截)
log.d(tag, "${prop.name}: $old -> $new")
}
score = 90 // 输出 score: 0 -> 90
7.3 标准委托三:vetoable(可否决的赋值)
var age: int by delegates.vetoable(0) { _, old, new ->
new >= 0 // 返回 false 则本次赋值被拒绝
}
age = 25 // 生效
age = -5 // 被拒绝,age 仍是 25
7.4 标准委托四:delegates.notnull()(延迟赋值 + 非空)
var token: string by delegates.notnull()
fun settoken(t: string) { token = t }
fun use() {
// 未赋值就读取会抛 illegalstateexception,比可空类型更安全
println(token.length)
}
7.5 委托给 map(适合解析 json map)
class config(map: map<string, any?>) {
val host: string by map
val port: int by map
}
val cfg = config(mapof("host" to "192.168.2.40", "port" to 1680))
println(cfg.host) // 192.168.2.40
println(cfg.port) // 1680
7.6 接口委托(by而不是属性)
interface logger {
fun log(msg: string)
fun logerror(e: throwable)
}
class defaultlogger : logger {
override fun log(msg: string) = println("[info] $msg")
override fun logerror(e: throwable) = println("[error] $e")
}
// 只需要重写一部分,其余全部转发给 impl
class orderviewmodel(private val impl: logger = defaultlogger()) : logger by impl {
override fun logerror(e: throwable) {
super.logerror(e) // 转发给被委托对象
crashreporter.report(e) // 额外增强
}
}
7.7 自定义委托(实现readwriteproperty/readonlyproperty)
// 场景:sharedpreferences 属性化读写
class preferencedelegate<t>(
private val prefs: sharedpreferences,
private val key: string,
private val default: t
) : readwriteproperty<any?, t> {
@suppress("unchecked_cast")
override fun getvalue(thisref: any?, property: kproperty<*>): t =
when (default) {
is string -> prefs.getstring(key, default) as t
is int -> prefs.getint(key, default) as t
is boolean -> prefs.getboolean(key, default) as t
is long -> prefs.getlong(key, default) as t
else -> error("不支持的类型: ${default!!::class.java}")
}
override fun setvalue(thisref: any?, property: kproperty<*>, value: t) {
prefs.edit().apply {
when (value) {
is string -> putstring(key, value)
is int -> putint(key, value)
is boolean -> putboolean(key, value)
is long -> putlong(key, value)
else -> error("不支持的类型")
}
}.apply()
}
}
// 使用
class settings(context: context) {
private val prefs = context.getsharedpreferences("app", context.mode_private)
var isdarkmode: boolean by preferencedelegate(prefs, "dark_mode", false)
var username: string by preferencedelegate(prefs, "user_name", "guest")
}
val s = settings(context)
s.isdarkmode = true // 自动写入 sp
println(s.isdarkmode) // 自动读取
8.by在 android 中的实战
8.1by viewmodels()/by activityviewmodels()
class orderfragment : fragment(r.layout.fragment_order) {
// 作用域绑定 fragment,配置变更不重建
private val viewmodel: orderviewmodel by viewmodels()
// 作用域绑定宿主 activity(fragment 之间共享数据)
private val sharedviewmodel: sharedviewmodel by activityviewmodels()
// 需要工厂时
private val vm: orderviewmodel by viewmodels {
orderviewmodelfactory(repository)
}
}
需要额外依赖:implementation "androidx.fragment:fragment-ktx:1.x.x"
8.2by lazy+ viewbinding
class orderfragment : fragment(r.layout.fragment_order) {
private var _binding: fragmentorderbinding? = null
private val binding get() = _binding!!
override fun oncreateview(...): view {
_binding = fragmentorderbinding.inflate(inflater, container, false)
return binding.root
}
override fun ondestroyview() {
super.ondestroyview()
_binding = null // fragment 视图销毁必须置空,否则内存泄漏
}
}
8.3by preference()(androidx.preference)
class settingsfragment : preferencefragmentcompat() {
val nickname: string by preference("nickname", "guest")
override fun oncreatepreferences(savedinstancestate: bundle?, rootkey: string?) {
setpreferencesfromresource(r.xml.settings, rootkey)
}
}
8.4by lazy+ kotlin 单例(object天然线程安全)
val retrofit: retrofit by lazy {
retrofit.builder()
.baseurl(baseconfig.base_url) // 例如 http://192.168.2.40:1680/api/q5-bill/
.addconverterfactory(gsonconverterfactory.create())
.addcalladapterfactory(rxjava3calladapterfactory.create())
.client(okhttpclient)
.build()
}
9. 其他高频工具函数
9.1takeif/takeunless
// takeif: 条件成立返回对象本身,否则返回 null
val validage = age.takeif { it in 0..150 }
// 结合 ?.let 使用,替代 if-else
userinput.takeif { it.isnotblank() }?.let { submit(it) }
// takeunless: 与 takeif 相反,条件不成立才返回对象
val file = file(path).takeunless { it.isdirectory }
9.2runcatching(替代 try-catch)
val result = runcatching {
// 可能抛异常的代码
val json = api.getorder(id)
parse(json)
}
result
.onsuccess { order -> render(order) }
.onfailure { e -> showerror(e.message) }
// 取值:getordefault / getornull / getorelse
val order = result.getornull()
val safe = result.getorelse { order.empty() }
9.3use(自动关闭资源,类似 java try-with-resources)
fileinputstream(file).use { input ->
val bytes = input.readbytes()
parser.parse(bytes)
} // 块结束自动 close,异常也会关闭
// 一次性发送请求的场景
httpurlconnection(url).apply {
connecttimeout = 5000
}.use { conn ->
conn.inputstream.bufferedreader().readtext()
}
9.4measuretimemillis/measurenanotime(性能计时)
val cost = measuretimemillis {
formulas.foreach { it.evaluate() }
}
log.d(tag, "公式计算耗时:${cost}ms")
9.5repeat(固定次数循环)
repeat(3) { index ->
log.d(tag, "第 ${index + 1} 次重试")
}
9.6isinitialized(lateinit安全检查)
private lateinit var adapter: orderadapter
fun refresh() {
if (::adapter.isinitialized) adapter.notifydatasetchanged()
}
以上就是android kotlin开发中常用函数使用手册的详细内容,更多关于android kotlin常用函数的资料请关注代码网其它相关文章!
发表评论