原文链接

By baeldung

1. 概览

在本教程中,我们将讨论Kotlin Contracts。 它的语法还不稳定,但是二进制实现是稳定的,并且Kotlin stdlib已经在使用了。

基本上,Kotlin contracts是一种通知编译器有关函数行为的方式。

2. Maven配置

Kotlin1.3版本引入此功能,所以我们需要使用1.3或更高的版本。本教程中,我们使用最新版本-1.3.0。

请参考Kotlin介绍获取更多关于配置的细节。

3. Contracts的目的

虽然像编译器一样聪明,但它不能总得出最佳结论。

考虑下面的例子:

data class Request(val arg: String)class Service {fun process(request: Request?) {validate(request)println(request.arg) // Doesn't compile because request might be null}
}private fun validate(request: Request?) {if (request == null) {throw IllegalArgumentException("Undefined request")}if (request.arg.isBlank()) {throw IllegalArgumentException("No argument is provided")}
}

阅读这段代码,任何码农都知道如果requestnull调用validate会抛出一个异常。换句话说,正常情况下应该调用println方法而不是抛NullPointerException异常。

不幸的是,编译器不知道这些,也不允许调用request.arg的引用。

但是,我们可以通过contract来增强validate方法,contract定义了如果函数成功返回(即,它没有引发异常),则给定的参数不为null

@ExperimentalContracts
class Service {fun process(request: Request?) {validate(request)println(request.arg) // Compiles fine now}
}@ExperimentalContracts
private fun validate(request: Request?) {contract {returns() implies (request != null)}if (request == null) {throw IllegalArgumentException("Undefined request")}if (request.arg.isBlank()) {throw IllegalArgumentException("No argument is provided")}
}

接下来,让我们更详细地了解此功能。

4. Contracts API

通用contract格式如下:

function {contract {Effect}
}

我们可以理解为“调用功能产生效果”。

4.1 根据返回值做保证

这里我们指定如果满足目标条件,则目标方法返回。我们在目的章节里使用这个准则。

我们还在returns中的指定一个值,该值将指示Kotlin编译器仅在返回目标值时才满足条件:

data class MyEvent(val message: String)@ExperimentalContracts
fun processEvent(event: Any?) {if (isInterested(event)) {println(event.message) }
}@ExperimentalContracts
fun isInterested(event: Any?): Boolean {contract { returns(true) implies (event is MyEvent)}return event is MyEvent
}

这有助于编译器在processEvent函数中进行智能转换。

注意当前return contracts连接的implies只允许true,false和null

implies接受Boolean参数,也只接受有效Kotlin表达式的子集:即,空检查(==null,!=null),实例检查(is,!is),逻辑操作符(&&,||,!)。

还有一个针对任何非返回值的变体:

contract {returnsNotNull() implies (event is MyEvent)
}

4.2 保证函数使用

callsInPlace contract表示如下保证:

  • 所有者函数完成后,不会调用callable
  • 不会传递给其他无contract的函数

这可以在以下情况下为我们提供帮助:

inline fun <R> myRun(block: () -> R): R {return block()
}fun callsInPlace() {val i: IntmyRun {i = 1 // Is forbidden due to possible re-assignment}println(i) // Is forbidden because the variable might be uninitialized
}

我们可以通过帮助编译器确保给定的块被调用且仅调用一次来解决错误:

@ExperimentalContracts
inline fun <R> myRun(block: () -> R): R {contract {callsInPlace(block, InvocationKind.EXACTLY_ONCE)}return block()
}

标准的Kotlin实用程序功能run,with,apply等已经定义了此类contracts。

这里我们使用了InvocationKind.EXACTLY_ONCE其他选项包括ATLEASTONCE, ATMOSTONCE, and UNKNOWN。

5. Contracts限制

尽管Kotlin contracts看起来很有希望,但目前的语法目前尚不稳定,并且有可能在将来完全更改。

而且,还有一些限制:

  • 只能将contracts应用于具有主体的顶级函数,即不能在字段和类函数上使用。
  • contract调用必须是函数体的第一个语句。
  • 编译器无条件相信contracts;这意味着码农负责编写正确合理的contracts。将来版本可能实现验证。

最后,contract描述只允许形参的引用。例如,下面代码无法编译:

data class Request(val arg: String?)@ExperimentalContracts
private fun validate(request: Request?) {contract {// We can't reference request.arg herereturns() implies (request != null && request.arg != null)}if (request == null) {throw IllegalArgumentException("Undefined request")}if (request.arg.isBlank()) {throw IllegalArgumentException("No argument is provided")}
}

6.结论

该功能看起来很有趣,即使其语法还处于原型阶段,该二进制表示形式也足够稳定,并且已成为stdlib的一部分。 如果没有一个适当的迁移周期,它就不会改变,这意味着我们可以依靠带有合同的二进制工件(例如stdlib)来获得所有通常的兼容性保证。

这就是为什么我们的建议现在就值得使用contracts——如果DSL改变,修改contract声明也不难。

像往常一样,本文中使用的源代码可从GitHub上获得。

A1. Effect源码

@ContractsDsl
@ExperimentalContracts
@SinceKotlin("1.3")
public interface Effect@ContractsDsl
@ExperimentalContracts
@SinceKotlin("1.3")
public interface ConditionalEffect : Effect@ContractsDsl
@ExperimentalContracts
@SinceKotlin("1.3")
public interface SimpleEffect : Effect {/*** Specifies that this effect, when observed, guarantees [booleanExpression] to be true.** Note: [booleanExpression] can accept only a subset of boolean expressions,* where a function parameter or receiver (`this`) undergoes* - true of false checks, in case if the parameter or receiver is `Boolean`;* - null-checks (`== null`, `!= null`);* - instance-checks (`is`, `!is`);* - a combination of the above with the help of logic operators (`&&`, `||`, `!`).*/@ContractsDsl@ExperimentalContractspublic infix fun implies(booleanExpression: Boolean): ConditionalEffect
}@ContractsDsl
@ExperimentalContracts
@SinceKotlin("1.3")
public interface ContractBuilder {/*** Describes a situation when a function returns normally, without any exceptions thrown.** Use [SimpleEffect.implies] function to describe a conditional effect that happens in such case.**/// @sample samples.contracts.returnsContract@ContractsDsl public fun returns(): Returns/*** Describes a situation when a function returns normally with the specified return [value].** The possible values of [value] are limited to `true`, `false` or `null`.** Use [SimpleEffect.implies] function to describe a conditional effect that happens in such case.**/// @sample samples.contracts.returnsTrueContract// @sample samples.contracts.returnsFalseContract// @sample samples.contracts.returnsNullContract@ContractsDsl public fun returns(value: Any?): Returns/*** Describes a situation when a function returns normally with any value that is not `null`.** Use [SimpleEffect.implies] function to describe a conditional effect that happens in such case.**/// @sample samples.contracts.returnsNotNullContract@ContractsDsl public fun returnsNotNull(): ReturnsNotNull/*** Specifies that the function parameter [lambda] is invoked in place.** This contract specifies that:* 1. the function [lambda] can only be invoked during the call of the owner function,*  and it won't be invoked after that owner function call is completed;* 2. _(optionally)_ the function [lambda] is invoked the amount of times specified by the [kind] parameter,*  see the [InvocationKind] enum for possible values.** A function declaring the `callsInPlace` effect must be _inline_.**//* @sample samples.contracts.callsInPlaceAtMostOnceContract* @sample samples.contracts.callsInPlaceAtLeastOnceContract* @sample samples.contracts.callsInPlaceExactlyOnceContract* @sample samples.contracts.callsInPlaceUnknownContract*/@ContractsDsl public fun <R> callsInPlace(lambda: Function<R>, kind: InvocationKind = InvocationKind.UNKNOWN): CallsInPlace
}


implies(booleanExpression: Boolean),booleanExpression必须为true

returns() implies condition,当conditiontrue时返回,否则会执行后续的逻辑,一般是抛出exception
returns(value: Any?) implies condition,当conditiontrue时返回预设值的值,否则执行后续逻辑。
returnsNotNull() implies condition,检查参数不为空。

Kotlin Contracts相关推荐

  1. Kotlin contract 用法及原理

    什么是 contract contract(契约)是一种 Kotlin 面向编译器约定的一种规则,它帮助编译器更加智能地识别某些需要特定的代码条件,为代码创建更加友好的上下文关联环境. Kotlin ...

  2. Kotlin 基础学习

    学! 原文:https://blog.csdn.net/CrazyApes/article/details/122091459 文章目录 Kotlin 线上编写 变量 lateinit延迟初始化 空安 ...

  3. 看不懂Kotlin源码?从Contracts 函数说起~

    前言 最近有朋友反馈说因为源码是Kotlin,所以看不懂.其实,很多时候看不懂Kotlin的源码很有可能是因为你不知道某些特定语法.正如你看不懂源码其实是因为不了解设计模式一样~ 举个例子 以Kotl ...

  4. Kotlin之契约Contracts

    Kotlin的Contracts是1.3引入的新功能,虽然还是试验阶段,但是在Kotlin的stdlib中已经有多处使用了(例如各种作用域函数).本文将带领大家解开这个神秘的"契约" ...

  5. 如何使您的Kotlin Android动画可访问

    When researching examples for a first ever Android contribution, few examples existed for animations ...

  6. Kotlin 中的 run、let、with、apply、also、takeIf、takeUnless 语法糖使用和原理分析

    这些Kotlin的语法糖函数经常用,但也很容易搞混,所以转载一下,若混了可以回来再看 转载自公众号:纸上浅谈 正文: 在 Kotlin 有一些可以简化代码的语法糖,比如 run.let.with.ap ...

  7. kotlin界面_Kotlin界面

    kotlin界面 In this tutorial, we'll be looking into interfaces in Kotlin. Kotlin interface is like cont ...

  8. 2020 年编程语言盘点展望:Java 老兵不死,Kotlin 蓄势待发

    在进入新的十年之际,各行各业都在进行盘点与展望.SegmentFault 作为开发者社区与科技行业的垂直媒体,一直关注行业的发展与相关动态,近期已陆续为大家整理了各大平台.社区针对技术领域作出的预测与 ...

  9. 为数不多的人知道的 Kotlin 技巧及解析(三)

    本文没有什么奇淫技巧,都是一些在实际开发中常用的技巧 Google 引入 Kotlin 的目的就是为了让 Android 开发更加方便,自从官宣 Kotlin 成为了 Android 开发的首选语言之 ...

最新文章

  1. win10安装java1.8开发环境JDK
  2. day_work_02
  3. java线程——信号量(Semaphore)+障栅(CyclicBarrier)
  4. centos上如何装python_centos如何安装Python3
  5. 中文分词——正向最大匹配法
  6. Linux内存管理:MMU那些事儿
  7. FFmpeg学习(6)——视频拼接
  8. 分析Linux内核创建一个新进程的过程
  9. HiJson(Json格式化工具)
  10. 作曲大师2019破解版|作曲大师音乐梦想家2019破解版下载 v2019.9(附安装破解图文教程)
  11. Python: PS 图像特效 — 模糊玻璃
  12. Inside-OutsideNet
  13. 带你一起撸一遍 nodejs 常用核心模块(一)
  14. Sparse Local Patch Transformer for Robust Face Alignment and Landmarks Inherent Relation Learning
  15. [收藏]三国时代的十大遗言
  16. Dev c++与vs
  17. consistent equation
  18. 最浅显易懂的数据库索引讲解
  19. 5G通信基站对邻频C波段卫星地球站干扰的分析与处置
  20. cad在线转换_CAD如何转换?一招教你在线免费将CAD转成多种格式

热门文章

  1. JavaScript实现归并排序算法并详解
  2. 七夕!有个程序员老公有多爽???
  3. 解决ios下拍照自动旋转问题
  4. 10年职场COO:小白转行新媒体运营,这些干货你必须记住-建议收藏
  5. 孪生素数100java_java实现孪生素数
  6. shell find命令用法
  7. 鸿蒙os3.0评测,华为P50Pro+:徕卡3+2双环镜头,麒麟9000芯片配鸿蒙OS3.0
  8. 【python帮你做学霸】统计英语作文和口语表达的“词汇丰富度”
  9. EOS中多数据源配置使用示例
  10. Ue 解决滑步的方案