当前位置:网站首页>看不懂Kotlin源码?从Contracts 函数说起~
看不懂Kotlin源码?从Contracts 函数说起~
2022-06-11 12:55:00 【黄林晴】
前言
最近有朋友反馈说因为源码是Kotlin,所以看不懂。其实,很多时候看不懂Kotlin的源码很有可能是因为你不知道某些特定语法。正如你看不懂源码其实是因为不了解设计模式一样~
举个例子
以Kotlin中常用的isNullOrEmpty方法为例,源码如下所示:
@kotlin.internal.InlineOnly
public inline fun CharSequence?.isNullOrEmpty(): Boolean {
contract {
returns(false) implies ([email protected] != null)
}
return this == null || this.length == 0
}
咦?代码很简单,不过怎么看不懂呢?contract是什么鬼,implies 又是什么鬼? 其实当你了解contract函数的使用方法之后,类似的源码你就都能看懂了。
Contracts是什么?
Contracts是合同、契约的意思。从Kotlin1.3版本的时候就被引入了,简单的来说Contracts可以用来解决一些编译器无法完成的功能。

所以,它到底是干嘛的呢?
我们来定义一个类似的函数,用于检测某个对象是否为null,首先定义一个User对象,代码如下所示:
data class User(val name: String, val age: Int)
再定义一个检查User对象是否为空的方法,代码如下所示:
fun isEmpty(user: User?) {
if (user == null) {
throw IllegalArgumentException("is empty")
}
}
然后我们在业务方法中调用,代码如下所示:
fun work(user: User?){
isEmpty(user = user)
setText(user.name)
}
此时这个方法是无法编译通过的,编译器会提醒你user是一个可为空的对象,需要添加"?.",

但是从我们的代码逻辑来看,我们首先调用了isEmpty方法,如果user对象为null的话,则在isEmpty方法中已经抛出了异常,也就是说不会走到setText方法中。换句话说,如果代码可以执行到setText这一行那么user对象肯定是不为空的。那么现在该如何处理呢?这就需要Contracts出场了。
Contracts的使用方法
Contracts API 一般如下所示:
fun A() {
contract {
}
}
当前主要使用方式有returns、callsInPlace等,首先我们来看returns的使用场景。
Returns Contracts
contract {
returns($value) implies($condition)
}
returns用来告诉编辑器当返回值为value时 condition条件成立。当前value可以是布尔类型或者空类型,condition条件表达式也需要返回布尔类型。
比如,我们现在修改isEmpty方法,代码如下所示:
@ExperimentalContracts
fun isEmpty(user: User?) {
contract {
returns() implies (user != null)
}
if (user == null) {
throw IllegalArgumentException("is empty")
}
}
这里我们通过contract约束告诉编译器,如果isEmpty函数可以正常返回的话,那么说明user不为空。换句话理解,也就说当user为空的时候由于抛出了异常,所以isEmpty函数是无法返回的。
你会发现,当修改了isEmpty方法之后,work方法已经不再报错了。

这是因为我们已经通过isEmpty方法告诉编译器,若代码可以执行到setText,说明user对象一定不为空。由于这个函数一直是实验性的API,所以这里要加上@ExperimentalContracts注解。
不过,目前Kotlin源码中已经很多用到了这个API,所以我们不用担心以后会发生大的变化。接着我们再来看CallInPlace的使用场景。
CallInPlace Contracts
CallInPlace的使用也是很广泛的,比如我们在Kotlin中常用的标准函数apply、also等。这里以apply函数为例,apply函数源码如下所示:
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
return this
}
从源码中我们可以看到,里面使用到了callsInPlace方法。callsInPlace可以告诉编辑器block方法仅会执行一次。而不是0次或多次。当然也可以设置方法至少执行一次或最多执行一次。这个读者可自行尝试。
Contracts的源码
首先我们来看下contract函数的源码,如下所示:
@ContractsDsl
@ExperimentalContracts
@InlineOnly
@SinceKotlin("1.3")
@Suppress("UNUSED_PARAMETER")
public inline fun contract(builder: ContractBuilder.() -> Unit) { }
contract是一个内联函数,需要一个ContractBuilder对象,接着来看ContractBuilder对象源码,如下所示:
@ContractsDsl
@ExperimentalContracts
@SinceKotlin("1.3")
public interface ContractBuilder {
@ContractsDsl public fun returns(): Returns
@ContractsDsl public fun returns(value: Any?): Returns
@ContractsDsl public fun returnsNotNull(): ReturnsNotNull
@ContractsDsl public fun <R> callsInPlace(lambda: Function<R>, kind: InvocationKind = InvocationKind.UNKNOWN): CallsInPlace
}
我们可以看到returns方法返回了Returns,callsInPlace方法返回了CallsInPlace,而Returns对象是SimpleEffect的接口实现自接口Effect,CallsInPalce对象是Effect接口,源码如下所示:
@ContractsDsl
@ExperimentalContracts
@SinceKotlin("1.3")
public interface SimpleEffect : Effect {
@ContractsDsl
@ExperimentalContracts
public infix fun implies(booleanExpression: Boolean): ConditionalEffect
}
@ContractsDsl
@ExperimentalContracts
@SinceKotlin("1.3")
public interface CallsInPlace : Effect
从SimpleEffect源码中可以看出,它实现了一个中缀函数名字为 implies,来告诉编译器booleanExpression将成立,以此来和编译器约定好。
写在最后
contract其实是通过与编译器达成约定的方式,弥补了智能转化的短板。
近期可能会有些许事情发生,借用“秉心说”大佬今天说的一句话就是 —— 2022年,好事多磨吧~
边栏推荐
- [problem summary] $t
- Go语言学习之WaitGroup用法详解
- Which 4K projector is the most cost-effective? When the Bay X3 Pro highlights the 128G storage 618, it is worth seeing
- Seckill multi-level cache ----- product details page
- Master-slave replication of MySQL
- Is it safe to open an account online in 2022?
- Condition debug of pycharm
- shader着色器
- Meichuang technology data security management platform won the "leading scientific and Technological Achievement Award" of 2022 digital Expo
- 2020.10.27 北京阿里大文娱一面总结
猜你喜欢

Unity game protection "big training", read and understand the game's pre defense

Clear the selected data in the modal box after the modal box is closed

. 4 literal and variable

一个时代的终结!十年了吴恩达经典《机器学习》课程本月关闭注册,上线新课!...

历史上的今天:Apple II 问世;微软收购 GECAD;发明“软件工程”一词的科技先驱出生...

Security mechanism of verification code in seckill
![[bug resolution] the form is paged to display the total data res.data total](/img/92/1ddde16d35465f8dd53ebf90e249b8.png)
[bug resolution] the form is paged to display the total data res.data total

What scenarios can the member management system of the multi guest swimming pool achieve?

TeaTalk·Online 演讲实录 | 圆满完结!安全上云,选对数据迁移策略很重要

openharmony标准系统移植之音频适配
随机推荐
启封easy QF PDA帮助企业提升ERP的管理水平
Audio adaptation of openharmony Standard System Porting
關於分布式鎖的續命問題——基於Redis實現的分布式鎖
NFT市场怎么样 为什么NFT能如此火爆 怎么搭建NFT平台
DB2数据库重建及表数据迁移探讨研究
How does go reduce supply chain attacks?
[backtrader source code analysis 46] cerebro Py code comments (boring, one of the core backtrader codes, recommended for reading, comments for reference only)
马斯克称自己不喜欢做CEO,更想做技术和设计;吴恩达的《机器学习》课程即将关闭注册|极客头条...
Node creates a template file with the art template template template engine
[ArcGIS]城市关联度分析
31W contest question bonus! When AI for Science collides with the "pilot Cup", what sparks will be generated?
【backtrader源码解析46】cerebro.py代码注释(枯燥,backtrader核心代码之一,推荐阅读,注释仅供参考)
Application of pip2pi, pypiserver and Apache in PIP local source configuration
What are the elements of running a gymnasium?
[noip1998] spelling
pip2pi和pypiserver及Apache在pip本地源配置中的应用实践
. 5 string
历史上的今天:Apple II 问世;微软收购 GECAD;发明“软件工程”一词的科技先驱出生...
Which 4K projector is the most cost-effective? When the Bay X3 Pro highlights the 128G storage 618, it is worth seeing
Number selection (greed)