Kotlin Contracts

Kenny Hadisaputra
4 min readApr 12, 2020

In Kotlin 1.3, there are many newly introduced features that are still experimental. Some of the features are really powerful that it can definitely help us write better and readable code.

In this article, I’m gonna explain one of the most powerful feature, which helps us remove some additional checks but also can be a double-edge sword for us and must be used carefully, Kotlin Contracts.

What are Kotlin Contracts?

Photo by Cytonn Photography

If we quote from the kotlinlang.orgWhat’s New in Kotlin 1.3”

(Kotlin) Contracts allow a function to explicitly describe its behavior in a way which is understood by the compiler.

This means that we have the capability to explicitly tells the compiler that we want certain things to behave as how we want it to. Currently these things are limited to

  1. Improving smartcast analysis
  2. Improving variable initialization analysis

Improving Smartcast Analysis

Let’s say we want to print the length of the string if that string is an email.

Without Kotlin Contracts

Here, we create an extension function to check whether the string is an email or not by returning a boolean. If it returns true, it means that it is definitely a valid email (which is not null and comply to email format) and false otherwise.

So we check the string by using the isEmail() extension function, and if true, we will print the length. But when we compile the code in the gist, it will not compile as it will give us an error “Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String?” on the email.length .

Why is that happening? Aren’t we sure that if isEmail returns true, then the string will definitely not null? As developer, we are sure that it is not null when isEmail() returns true, but the compiler doesn’t know that !

The solution? We can simply just use double bang email!!.length and it will compile fine because we are sure that it is safe from our scenario. But… you know, we always try to avoid the !! from our code as much as possible.

Well then, let’s make a contract…

Here, we can leverage the power of Kotlin Contracts to make some sort of agreement with the compiler. From the case above, the agreement we wanted to make with the compiler is “if isEmail() returns true, it is definitely not null”.

With Kotlin Contracts

In the gist above, I add contract{} function which will define the agreement that we want. The code inside the contract{}lambda is pretty self-explanatory.

returns(true)specifies the condition of the agreement and implies (this@isEmail != null)describes the point of the agreement if the condition is met. So in this case, if extension function isEmail()returns boolean value true, it means the receiver string (aaa@gmail.com) should be treated as non-null. Then we can remove the !!and it will compile just fine.

To compile Kotlin Contracts in Kotlin 1.3.70, you need to opt in by adding this as compiler argument -Xopt-in=kotlin.contracts.ExperimentalContracts. For Kotlin version below 1.3.70, add -Xuse-experimentalinstead.

Mind-blowing, isn’t it?

Improving Variable Initialization Analysis

So, we’ve seen how Kotlin Contracts can help us by telling the compiler to smartcast it if certain condition is met. It’s not all that Kotlin Contracts can do.

Supposedly, you would want to log the start and end of something you done.

Yeah, why would you want to log the assignment of email? lol

This code will not compile as the compiler will give us an error “Kotlin: Variable ‘email’ must be initialized”.

Why is that happening? The compiler doesn’t actually know if the block inside logToConsolewill be called and so the compiler thinks that the variable emailmight not be initialized.

Yes, we are sure that the block inside will be initialized, but the compiler doesn’t. So what should we do?

Just like before, we need to make a contract.

This time, we want to make an agreement to the compiler that the block inside logToConsolewill be called/invoked.

We assured the compiler that block will definitely be invoked once

In the gist above, I add contract{}function and again the code inside the lambda is pretty self-explanatory.

callsInPlace(block, InvocationKind.EXACTLY_ONCE)means that we assure the compiler that the block will be called exactly one time. There are some other InvocationKind such as AT_MOST_ONCE, AT_LEAST_ONCE, and UNKNOWN.

By doing this, we can now compile this code and it will print the length of the email.

The double edge — What if we lied?

What if we lied about the agreement that we made with the compiler?

Let’s lie to the compiler.

Breaking the agreement

So, here we have the same code as the last gist, but you can see that inside logToConsole(logTag, block)function, I don’t actually call the block even though I told the compiler that it will be called once.

Will this compile? Sure this one will compile, but emailwill be null and so it will not print the length, even though I did not set emailto null.

This is what I want to warn you. The compiler will blindly trust any contracts that we make and so we have to adhere to the contract itself. By not respecting the contract, we might have to deal with weird state on runtime.

So, that’s all that I want to share. There are still many more things that you can explore from Kotlin Contracts and I hope if you use it, use it carefully and please respect the contract that you make.

--

--

Kenny Hadisaputra

Android Developer and Kotlin Enthusiast (can also do a little bit iOS)