Skip to Content
Course content

47: Contracts for Smart Compiler Analysis

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I've seen this exact scenario play out in countless pull requests. You're trying to keep your code DRY by moving a null check or a validation check into a helper function, but the Kotlin compiler suddenly starts fighting you. You know the value isn't null, but the compiler refuses to believe you.

The Smart Cast that Refuses to Cast

Take a look at this snippet. I'm trying to build a simple validator for a user profile. I want to make sure the username isn't null or empty before I process it.

fun isUsernameValid(name: String?): Boolean {
    return !name.isNullOrBlank()
}

fun processProfile(username: String?) {
    if (isUsernameValid(username)) {
        // ERROR: Smart cast to 'String' is impossible, because 'username' is a mutable property 
        // (or in this case, the compiler just doesn't know what happened inside the function)
        println("Processing user: ${username.length}") 
    }
}

If you try to compile this, you'll get a type mismatch error on username.length. Even though isUsernameValid returns true only if the string is not null, the compiler isn't "smart" enough to look inside that function and carry that knowledge back to the processProfile scope. To the compiler, isUsernameValid is just a black box that returns a boolean; it has no idea that the boolean is logically tied to the nullability of the input.

Teaching the Compiler with Contracts

This is where kotlin.contracts comes in. Contracts allow you to explicitly tell the compiler: "Hey, if this function returns true, you can safely assume this variable is not null." It's basically a way of documenting the behavior of your function in a way that the compiler can actually use for static analysis.

Here is how we fix the validation logic. We need to import kotlin.contracts.contract and kotlin.contracts.returns.

import kotlin.contracts.*

fun isUsernameValid(name: String?): Boolean {
    contract {
        returns(true) implies (name != null)
    }
    return !name.isNullOrBlank()
}

fun processProfile(username: String?) {
    if (isUsernameValid(username)) {
        // Now this works! The compiler knows username is String, not String?
        println("Processing user: ${username.length}")
    }
}

By adding that contract block, we've bridged the gap. The line returns(true) implies (name != null) tells the compiler that a true result is a guarantee of non-nullability. Now, the smart cast triggers perfectly inside the if block.

When to Actually Use This

I'll be honest: you won't use contracts in every single function. Most of the time, Kotlin's standard library handles this for you (which is why ?.let {} or if (x != null) just work). However, you should reach for contracts when:

  • You are building a library or a shared utility module where other developers will rely on your validation helpers.
  • You have complex validation logic that would be messy to repeat inside every if statement.
  • You want to avoid the "double-bang" operator (!!) without wrapping every single call in a let block.

Just a heads-up: contracts are currently an experimental feature (though very stable in practice). You might see a compiler warning asking you to opt-in using @OptIn(ExperimentalContractsApi::class) depending on your project configuration. Don't let that scare you; it's the standard way to handle these specialized compiler hints.




📋 Practical Task

Implement a Non-Null Guard for Order Processing

You are building an e-commerce checkout system. You have a OrderRequest data class where the shippingAddress is optional. You've written a helper function called isReadyForShipping that checks if the address is present and not empty.

Currently, the code below fails to compile because the compiler doesn't know that a true return from isReadyForShipping means the address is not null. Your task is to implement a Contract inside isReadyForShipping to enable the smart cast in the shipOrder function.

import kotlin.contracts.*

data class OrderRequest(val orderId: Int, val shippingAddress: String?)

fun isReadyForShipping(request: OrderRequest): Boolean {
    // TODO: Add a contract here that implies request.shippingAddress != null if returns true
    return !request.shippingAddress.isNullOrBlank()
}

fun shipOrder(request: OrderRequest) {
    if (isReadyForShipping(request)) {
        // This line currently causes a compiler error: 
        // Smart cast to 'String' is impossible
        println("Shipping order ${request.orderId} to ${request.shippingAddress.uppercase()}")
    } else {
        println("Order ${request.orderId} is missing an address.")
    }
}

fun main() {
    val validOrder = OrderRequest(101, "123 Kotlin Lane")
    val invalidOrder = OrderRequest(102, null)
    
    shipOrder(validOrder)
    shipOrder(invalidOrder)
}
Rating
0 0

There are no comments for now.

to be the first to leave a comment.