Kotlin 中的 lateinit 、lazy、let 、apply 、also 、takeif、takeUnless

本文深入探讨Kotlin中的高级特性,包括lateinit与lazy的延迟初始化技术,以及let、repeat、with、apply、run、also、takeIf和takeUnless等实用函数的用法与场景。通过实例解析,帮助读者掌握这些特性,提升编程效率。

前提

Kotlin和Groovy等语言一样,支持闭包(block),如果函数中最后一个参数为闭包,那么最后一个参可以不写在括号中,而写在括号后面,如果只有一个参数,而在之后的调用中参数没有被调用,则括号也可以去掉。

viewPager.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

            }
       });      

那么在 Kotlin 中,我们可以得到如下代码

view.setOnClickListener ({
        print("name")
    })

只有一个参数,括号可以去掉

view.setOnClickListener {
        print("name")
    }

lateinit 、by lazy

在 Kotlin 中 lateinit 和 lazy 是两种不同的延迟初始化 技术。

  • lateinit 只用于 var,而 lazy 只用于 val

  • lazy 应用于单例模式(if-null-then-init-else-return),而且当且仅当变量被第一次调用的时候,委托方法才会执行。

  • lateinit 则用于只能生命周期流程中进行获取或者初始化的变量,比如 Android 的 onCreate()

  • lateinit 只能修饰非 kotlin 基本类型,在 kotlin 中会以 null 初始化由lateinit 修饰的变量,而基础类型没有null 。

  • 使用 lateinit 修饰的变量,在使用时不必使用 ?!! 修饰,但是必须在第一次使用时保证为变量赋初值,不然程序会报出空指针异常。

  • 使用 by lazy 修饰的变量:

val name by lazy{
	name.....
} 

在第一次使用 name 时,会执行闭包中的代码对变量 name 进行初始化。

let

以当前这个对象作为闭包的 it 参数,返回值是函数里面最后一行,或者指定 return 。

var str = listof(3,3,6)
fun main(args: Array<String>){
	str.let{
	  println(it)
	  println(it)
	  return
	}
}

打印记录:

[3, 3, 6]
[3, 3, 6]

repeat

重复执行闭包里面的内容

repeat(3){
   println("name")
}

打印信息如下:

name
name
name

with

Calls the specified function block with the given receiver as its receiver and returns its result.

使用给定接收器作为接收器,调用指定的功能块并返回其结果。

它是将某对象作为函数的参数,在函数块内可以通过 this 指代该对象。返回值为函数块的最后一行或指定return表达式。

val name  = with("mike"){
	print(this)
	"Job"
}
print(name)

// 日志
mike
Job
apply

调用某对象的 apply 函数,在该函数的范围内,可以使用该对象的任意函数,并且返回该对象。

 ArrayList<Int>().apply {
        add(1)
        add(2)
        add(3)
        println(this)
    }.let { println(it) }

打印结果:

[1, 2, 3]
[1, 2, 3]

run

run 函数的使用基本和 apply 函数相同,唯一不同的是,run 函数的返回值由最后一行决定,而 apply 函数返回该对象。

ArrayList<Int>().run {
        add(1)
        add(2)
        add(3)
        println(this)
        get(1)
    }.let { println(it.toString() + "3535") }

打印日志:

23535

also

执行闭包,返回调用对象 this。

ArrayList<Int>().run {
        add(1)
        add(2)
        add(3)
        println(this)
        get(1)
    }.also {
        println(it)
        it + 2
    }.let { println(it) }

执行结果

[1,2,3]
2
2

最后一个 2 的得到原因为:also 函数为执行闭包中操作后返回 this,那么在此处 this 便是在 run 函数中返回的元素 – 2。

takeif

如果 满足 闭包中的条件返回调用对象,否则返回 null 。

4.takeIf {
        it > 3
    }.let {
        println(it)
    }

执行结果

4
如果具体代码如下:

2.takeIf {
        it > 3
    }.let {
        println(it)
    }

则执行结果为

null

takeUnless

如果 不满足 闭包中的条件返回调用对象,否则返回 null 。
与 takeif 中相同的例子,得到的结果正好是相反的。

<think>嗯,用户想了解Kotlin中与'detail'相关的概念或用法。首先,我需要明确“detail”这个词在Kotlin中的具体指向。可能是指细节处理、底层实现、或者特定功能的详细用法?用户可能在学习过程中遇到了一些需要深入理解的细节问题,比如作用域函数、密封类、内联类等高级特性。 接下来,我需要回忆Kotlin的核心特性中哪些部分常被提及为“细节”或需要特别注意的地方。作用域函数如let、run、apply等,它们在简化代码的同时,也有不同的适用场景和细微差别。密封类(Sealed Class)在状态管理中的使用,可能涉及到详尽的模式匹配。还有内联类(Inline Class)的轻量封装,这些都属于比较细节但重要的概念。 另外,用户可能对Kotlin协程的上下文切换、挂起函数的具体实现机制感兴趣。这些底层细节对于优化异步代码性能很重要。委托属性(Delegated Properties)如lazy、observable,以及DSL构建中的@DslMarker注解,都是Kotlin特有的高级用法,可能符合用户的需求。 还要考虑用户可能的实际应用场景,比如如何处理空安全、扩展函数的使用细节,或者与Java互操作时的注意事项。例如,平台类型(Platform Types)在Kotlin中的处理方式,这些细节可能影响代码的健壮性。 需要参考提供的引用资料,但用户给出的引用似乎更多涉及函数、Dagger依赖注入、编译器实现示例、可视化辅助和PWA应用。虽然引用内容与Kotlin的直接关联不大,但可以借鉴引用[1]中关于Kotlin函数的详细讨论,引用[3]提到的编译器生成的代码结构,可能涉及内联类或密封类的底层实现细节。 用户可能希望了解如何在实际项目中应用这些细节,比如性能优化、代码简洁性提升或错误减少。因此,回答中需要结合具体代码示例,说明每个概念的应用场景和注意事项。例如,作用域函数的选择依据,密封类在when表达式中的优势,内联类如何避免运行时开销。 还需要生成相关问题,引导用户进一步探索。问题可能涉及性能分析、最佳实践、特定概念的应用场景等,帮助用户深入理解各个细节部分。 最后,确保回答结构清晰,分点说明每个细节概念,并附上代码示例,符合用户要求的格式和中文回答。同时检查LaTeX语法是否正确,数学表达式使用$...$格式,独立公式用$$...$$。引用标识要自然添加,但根据用户提供的引用内容,可能引用[1]和[3]较为相关,但需要确认是否准确关联。</think>在Kotlin中,与"detail"相关的核心概念通常涉及语言特性的底层实现、特定场景的优化策略或代码编写中的细节规范。以下是几个关键方向的技术解析: --- ### 一、作用域函数的细节选择 作用域函数`let/run/with/apply/also`的选择依据: ```kotlin // 场景1:非空对象处理(推荐使用let) user?.let { print(it.name) sendEmail(it) } // 场景2:对象配置(推荐apply) val dialog = AlertDialog.Builder(context).apply { setTitle("提示") setCancelable(false) }.create() // 场景3:返回计算结果(推荐run) val result = networkClient.run { connect() fetchData().parse() } ``` - `let`适合链式调用中的空安全检查[^1] - `apply`始终返回对象本身,适合初始化配置 - 注意避免过度嵌套导致可读性下降 --- ### 二、密封类的详尽性保障 密封类通过编译器强制检查分支完整性: ```kotlin sealed class NetworkResult { data class Success(val data: String) : NetworkResult() data class Error(val code: Int) : NetworkResult() object Loading : NetworkResult() } fun handleResult(result: NetworkResult) = when(result) { is NetworkResult.Success -> showData(result.data) is NetworkResult.Error -> showError(result.code) NetworkResult.Loading -> showProgressBar() // 无需else分支,编译器验证覆盖所有情况 } ``` - 相比枚举类,支持不同子类携带差异化数据 - 编译器会验证`when`表达式是否覆盖所有子类[^3] --- ### 三、内联类的零开销封装 通过`@JvmInline value class`实现类型安全且无运行时开销: ```kotlin @JvmInline value class Password(private val s: String) { init { require(s.length >= 8) { "密码至少8位" } } } fun login(pwd: Password) { /* 编译后参数类型变为String */ } ``` - 运行时等同基础类型(如String),避免对象创建开销 - 编译期提供类型检查,防止参数误用[^3] --- ### 四、协程上下文切换细节 通过`withContext`优化线程调度: ```kotlin suspend fun fetchData() = withContext(Dispatchers.IO) { val rawData = apiService.fetch() // IO线程执行 withContext(Dispatchers.Default) { parseData(rawData) // CPU密集型操作 } } ``` - 避免在IO线程执行计算密集型任务 - 上下文切换次数应最小化(推荐单次`withContext`包含多个同类型操作) --- ### 五、委托属性的实现机制 `lazy`委托的线程安全模式选择: ```kotlin val heavyConfig by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { loadConfigFromFile() // 同步初始化保证线程安全 } val uiState by lazy(LazyThreadSafetyMode.NONE) { MutableStateFlow(Loading) // 主线程单次初始化 } ``` - `SYNCHRONIZED`模式(默认)保证多线程安全但性能较低 - `NONE`模式在确认单线程访问时提升性能[^1] --- ### 六、类型系统的平台类型处理 Java互操作时的平台类型处理策略: ```kotlin // Java方法:public String getUserName() { ... } val name1 = javaObj.userName // 类型推断为String!(平台类型) val name2: String = javaObj.userName // 显式声明为非空 val name3: String? = javaObj.userName // 允许为空 ``` - 建议立即进行空检查或类型转换 - 使用`@Nullable/@NotNull`注解改善类型推断 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值