How do I use takeIf and takeUnless for conditional evaluations in Kotlin?

In Kotlin, takeIf and takeUnless are scope-style functions used to keep or discard a value based on a condition.

takeIf

takeIf returns the object itself if the predicate is true; otherwise it returns null.

val result = value.takeIf { condition }

Equivalent to:

val result = if (condition) value else null

Example

val number = 10

val evenNumber = number.takeIf { it % 2 == 0 }

println(evenNumber) // 10

If the condition fails:

val number = 7

val evenNumber = number.takeIf { it % 2 == 0 }

println(evenNumber) // null

takeUnless

takeUnless is the opposite of takeIf.

It returns the object itself if the predicate is false; otherwise it returns null.

val result = value.takeUnless { condition }

Equivalent to:

val result = if (!condition) value else null

Example

val number = 7

val notEvenNumber = number.takeUnless { it % 2 == 0 }

println(notEvenNumber) // 7

If the condition is true:

val number = 10

val notEvenNumber = number.takeUnless { it % 2 == 0 }

println(notEvenNumber) // null

Common use with safe calls

Because both functions can return null, they are often used with ?.let.

val input = "kotlin"

input
    .takeIf { it.length > 3 }
    ?.let {
        println("Valid input: $it")
    }

This prints:

Valid input: kotlin

If the condition fails, let is skipped:

val input = "hi"

input
    .takeIf { it.length > 3 }
    ?.let {
        println("Valid input: $it")
    }

Nothing is printed.

Practical examples

Validate a string

fun normalizeUsername(username: String): String? {
    return username
        .trim()
        .takeIf { it.length >= 3 }
        ?.lowercase()
}

Usage:

println(normalizeUsername("  Alice  ")) // alice
println(normalizeUsername("  a  "))     // null

Reject blank input

val name = userInput.takeUnless { it.isBlank() }

This keeps userInput only if it is not blank.

Equivalent to:

val name = if (!userInput.isBlank()) userInput else null

Parse only valid values

val age = input
    .toIntOrNull()
    ?.takeIf { it >= 0 }

This gives you a non-negative integer or null.

println("42".toIntOrNull()?.takeIf { it >= 0 })  // 42
println("-5".toIntOrNull()?.takeIf { it >= 0 })  // null
println("abc".toIntOrNull()?.takeIf { it >= 0 }) // null

Important note

The predicate runs on the object itself, available as it.

val text = "Kotlin"

val result = text.takeIf { it.startsWith("K") }

Here, it is "Kotlin".

When to use which

Use takeIf when you want to keep a value if a condition is true:

val activeUser = user.takeIf { it.isActive }

Use takeUnless when you want to keep a value unless a condition is true:

val visibleUser = user.takeUnless { it.isDeleted }

In short:

value.takeIf { predicate }     // value if predicate is true, else null
value.takeUnless { predicate } // value if predicate is false, else null