Kotlin语法糖

很多人会把Kotlin里面的很多语法,都称作Kotlin语法糖,而我更倾向于把Kotlin标准库的几个非常常用且非常有用的高阶函数称作语法糖,各有所爱哈,不喜勿喷~~

而我所说的就是let,with,run,apply,also。

语法糖(一) let。

@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(this)
}

以上是标准库Standard.kt中的let函数的实现。

稍作解释,它是一个泛型内联函数,函数类型符合上一篇中的A.(B) -> C这种形式。看到它的实现很简单,返回了block(this)。

因为它没有限定符,所有的对象都可以调用它。

        val a = 10
        a.let {
            print(it)
        }

当然,这样的用法体现不了它的优势,但是我们这里能说明问题就好。后面我会共享一个我针对Kotlin系列的博客,专门写的一个Android App,用Kotlin写的,有兴趣的童鞋到时候可以git pull一下。

就取一个let的例子过来。

                item?.let {
                    context?.contentResolver?.call(
                        ...,
                        Bundle()
                    )
                }

一般情况下,let都是配合?.来调用,就是说判断调用是否为null,如果不为null的话,执行let中的代码,而let有一个隐式的参数it,就指代已经判空的非空对象。需要注意的是,let的返回值是整个表达式的最后一行(拥有多行语句的情况下)。

语法糖(二)apply。

@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()
    return this
}

和let一样,也是一个泛型的扩展函数类型,意味着所有的对象都可以调用它。需要注意的是,apply的返回值是this,也就是调用对象本身,而不是let中的block(this)。

        password.apply {
            afterTextChanged {
                loginViewModel.loginDataChanged(
                    this.text.toString(),// This指代password.
                    text.toString()  ///可以省略this.
                )
            }
            login.setOnClickListener {
                loading.visibility = View.VISIBLE
                loginViewModel.login(username.text.toString(), text.toString())
            }
        }

apply中也有一个隐式参数this,指代的就是调用对象,因此在apply上下文中,如果需要使用调用对象,可以直接使用this,或者可以省略。

语法糖(三) run。

@kotlin.internal.InlineOnly
public inline fun <R> run(block: () -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}

@kotlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}

run在标准库中有两个实现,一个是run{},另一个是T.run()。

也就是说,run可以直接执行,或者使用有接收者的形式调用。如:

        val a = 10
        // 直接执行run.
        run { 
            println(a)
        }
        CacheManager.read(CacheCons.USER)?.run {  //使用T.run.
            LoggedInUserView.createFromParcel(this as Parcel).also {
                binding.user = it
            }
        } ?: run {  // 直接使用run.
            binding.user =
                LoggedInUserView(requireContext().getString(R.string.user_alias), "")
        }

和apply一样,run也有一个隐式的参数this,指代调用者或上下文this,如果直接使用run{}指代的就是当前类上下文。

run可以有返回值this,或者不返回。

语法糖(四)also。

@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.also(block: (T) -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block(this)
    return this
}

上面的例子有看到also,需要说明的是,also拥有一个隐式参数it,代表调用者。另外,also的返回值是调用者本身,从实现可以看出,block(this)对this进行了处理,最后返回了this。

语法糖(五) with。

@kotlin.internal.InlineOnly
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return receiver.block()
}

可以看到,with的实现和其中一个run一样,因此它不需要依赖调用者去.它,直接在上下文中使用即可,需要注意的是,它有两个参数,第一个参数receiver,第二个参数block,而且第二个参数的调用者就是第一个参数。

从实现中也可以看出,它返回的是第一个参数receiver.block()。

一般情况下,使用with的场景比较少。

相当于一个委托,将函数和对象传入,最终返回一个结果。

with中也有一个隐式的参数this,指代的是第一个泛型参数T。

    with(context){
        this.startActivity(Bundle().also {
            it.putString("ext", "1")
        })
        // 也可以省略this。
        //startActivity(Bundle().also {
        //    it.putString("ext", "1")
        //})
    }

基本最常用的语法糖就这几个啦,当然,在标准库中,有很多类似很好用的高阶函数,都可以称之为语法糖,只是这几个很常用且很有代表性。

是不是对具体的使用场景有些迷惑,在这里网上有大神针对此做出了总结,一起学习一下。

另外有兴趣的同学可以打开Standard.kt文件了解一下,比如takeIf, takeUnless等,还有基于Closable的use,简直不要太好用,有了 它,访问数据库、文件等,再也不用担心没有关闭,资源没有释放导致内存泄漏等一系列棘手问题啦。

至此,童鞋们已经可以游刃有余地使用Kotlin来写应用啦。基于Kotlin1.3,Kotlin的协程已经很稳定了,而且提供了像通道、扇入、扇出、生产者-消费者等一系列好用的能力。

下一篇,就来看看协程的魅力。因为小狮子主要做Android的,因此可能某些例子会直接使用Android中的相关代码,能说明问题就可以啦。

我建了一个QQ群(877735946),现在也没几个人=_=,有兴趣的童鞋可以加群一起学习讨论,如果我知道的,必将知无不言。

对于文章如果有描述或理解错误的,还望大神留言区多多指教,不胜感激+_+。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐