Question

Kotlin Void vs Unit vs Nothing: Differences, Uses, and Examples

kotlingenericstypesfunctional-programming

Question

Kotlin has three types that appear similar at first glance:

  • Void
  • Unit
  • Nothing

They can seem comparable to JavaScript concepts such as null, undefined, and void 0, which raises the question of whether they overlap unnecessarily.

What is each of these Kotlin types used for, and how do they differ in meaning and behavior?

Short Answer

By the end of this page, you will understand what Void, Unit, and Nothing mean in Kotlin, when each one appears, and why they are not interchangeable. You will also learn how these types are used in function return types, Java interoperability, and expressions that never complete normally.

Concept

Kotlin uses Unit, Nothing, and sometimes Void for different purposes, even though they can look related.

Unit

Unit is Kotlin's way of saying:

"This function finishes normally, but it does not return any useful value."

It is similar to Java's void, but with one important difference: Unit is a real type in Kotlin. It has exactly one value: Unit.

fun printMessage(): Unit {
    println("Hello")
}

In most Kotlin code, you omit it:

fun printMessage() {
    println("Hello")
}

Kotlin still treats that function as returning Unit.

Nothing

Nothing means:

"This code never successfully produces a value because it never completes normally."

A function returning Nothing might:

  • always throw an exception
  • enter an infinite loop
  • terminate the program

Example:

fun fail(message: String): Nothing {
    throw IllegalArgumentException(message)
}

This function never returns to the caller.

Nothing is useful in the type system because it tells Kotlin that execution stops here.

Void

Void is not the usual Kotlin choice. It mainly exists for Java interoperability.

In Java, Void is a class type (java.lang.Void), while void is a primitive-like return concept. Kotlin may use Void when interacting with Java APIs that explicitly mention Void.

Example context:

  • Java generic APIs like Callable<Void>
  • frameworks or libraries written in Java

In idiomatic Kotlin, you usually use Unit, not Void.

Why this matters

These types help Kotlin express intent more precisely:

  • Unit: normal completion, no meaningful result
  • Nothing: no completion at all
  • Void: Java interop type, usually avoid in pure Kotlin

This precision improves readability, compiler checks, and interoperability with Java libraries.

Mental Model

Think of a function like a delivery trip.

  • Unit means: the driver came back, but there was no package to deliver.
  • Nothing means: the driver never comes back at all.
  • Void means: you are working with another company's paperwork system (Java), and they use a special form named Void.

So even though all three may look like "no value," they describe very different situations:

  • one finishes normally
  • one never finishes normally
  • one mostly exists to match Java APIs
Take Quiz

Syntax and Examples

Core syntax

Unit

fun log(message: String): Unit {
    println(message)
}

Usually written without the explicit return type:

fun log(message: String) {
    println(message)
}

Nothing

fun crash(): Nothing {
    throw RuntimeException("Something went wrong")
}

Void

fun javaStyle(): Void? {
    return null
}

This is uncommon in Kotlin and usually only appears when matching Java APIs.

Beginner-friendly examples

Example 1: Unit

fun greet(name: String) {
    println("Hello, $name")
}

This function does work and ends normally. It returns Unit.

Example 2: Nothing

fun missingUser(): Nothing {
    throw IllegalStateException("User not found")
}

This function never returns a result because it always throws.

Example 3: Void in interop situations

val task: java.util.concurrent.Callable<Void> = java.util.concurrent.Callable {
    println("Running task")
    null
}

Here, Void is used because the Java interface expects it.

A useful expression example

Because throw has type Nothing, it can be used where another type is expected:

fun getName(name: String?): String {
    return name ?: throw IllegalArgumentException("Name is required")
}

The right side of ?: is Nothing, so Kotlin accepts it as part of an expression that must produce a String.

Step by Step Execution

Consider this example:

fun getUsername(input: String?): String {
    return input ?: throw IllegalArgumentException("Username cannot be null")
}

Now trace it.

Case 1: input is not null

getUsername("alice")

Step by step:

  1. The function receives "alice".
  2. Kotlin checks input ?: ....
  3. Since input is not null, the left side is used.
  4. The function returns "alice".
  5. The function completes normally.

Case 2: input is null

getUsername(null)

Step by step:

  1. The function receives null.
  2. Kotlin checks input ?: ....
  3. Since input is null, Kotlin evaluates the right side.
  4. The right side throws IllegalArgumentException.
  5. The function does not return a String value.
  6. Execution stops at that point.

The key idea is that throw has type Nothing, so Kotlin knows there is no normal return path there.

Now compare with Unit:

fun printUsername(input: String?) {
    println(input ?: "guest")
}

Step by step:

  1. The function prints either the provided username or "guest".
  2. It finishes normally.
  3. It returns Unit.

So:

  • Unit = completed normally
  • Nothing = never completed normally

Real World Use Cases

Where Unit is used

Unit appears in functions that perform actions:

  • logging
  • printing output
  • saving data
  • sending analytics events
  • updating UI state
  • writing files

Example:

fun saveUser(user: User) {
    repository.save(user)
}

The function does something useful, but there is no meaningful value to return.

Where Nothing is used

Nothing is common in code paths that must stop execution:

  • validation failures
  • impossible states
  • helper functions that always throw
  • program termination wrappers

Example:

fun requireAdmin(isAdmin: Boolean): Nothing {
    throw IllegalAccessException("Admin access required")
}

A more realistic pattern is a helper used conditionally:

fun unauthorized(): Nothing {
    throw IllegalStateException("Unauthorized")
}

Then:

val user = currentUser ?: unauthorized()

Where Void is used

Void shows up mostly when Kotlin talks to Java libraries:

  • Java generics
  • callback APIs
  • old framework interfaces
  • executor tasks returning no meaningful value

Example:

val callable = java.util.concurrent.Callable<Void> {
    println("Background work")
    null
}
Take Quiz

Real Codebase Usage

In real Kotlin projects, developers usually follow these patterns.

Unit for side-effect functions

Most application code uses Unit implicitly:

fun updateProfile(profile: Profile) {
    validate(profile)
    api.save(profile)
}

This is the normal choice for command-style functions.

Nothing for guard clauses and fail-fast code

A common pattern is writing helper functions that stop execution:

fun failValidation(message: String): Nothing {
    throw IllegalArgumentException(message)
}

Then using them inside expressions:

val email = inputEmail ?: failValidation("Email is required")

This is especially useful for:

  • validation
  • null checking
  • impossible branches
  • exhaustive when handling

Nothing and unreachable code

Developers use Nothing to signal that a branch cannot continue:

fun parseRole(role: String): String = when (role) {
    "admin" -> "Administrator"
    "user" -> "Standard User"
    else -> throw IllegalArgumentException("Unknown role")
}

The else branch has type Nothing, which fits because it never returns normally.

Void only when interop requires it

In production Kotlin code, Void is typically avoided unless a Java API demands it.

For example:

val futureTask = java.util.concurrent.Callable<Void> {
    doCleanup()
    null
}

If you are writing Kotlin-first APIs, prefer Unit over Void.

Practical rule used by teams

  • Use Unit for ordinary no-result functions.
  • Use Nothing for code that always throws or never ends.
  • Use Void only to match Java types.

Common Mistakes

1. Using Void instead of Unit in Kotlin code

Broken style:

fun logMessage(): Void? {
    println("Hello")
    return null
}

Better:

fun logMessage() {
    println("Hello")
}

Why this is a mistake:

  • Void is not idiomatic Kotlin
  • it often forces null
  • it makes APIs less natural

2. Thinking Unit means null

Unit is a real value, not the same as null.

val result: Unit = Unit

null means absence of a value. Unit means a specific no-useful-result value.

3. Thinking Nothing is the same as "returns nothing"

Broken understanding:

fun test(): Nothing {
    println("Hi")
}

This is invalid because the function completes normally.

Correct usage:

fun test(): Nothing {
    throw IllegalStateException("Stops here")
}

4. Returning null for Nothing

Broken code:

fun bad(): Nothing {
    return null
}

This is impossible. Nothing has no values.

5. Forgetting that throw is an expression of type Nothing

Many beginners write extra code because they do not realize this:

Less clean:

fun getId(id: String?): String {
    if (id == null) {
        throw IllegalArgumentException("Missing id")
    }
    return id
}

Also valid, but Kotlin often allows a shorter expression form:

fun getId(id: String?): String {
    return id ?: throw IllegalArgumentException("Missing id")
}

6. Confusing Java void with Java Void

In Java:

  • void is a return-only concept
  • Void is a class type

In Kotlin, Unit usually maps to the no-result function case, while Void appears mainly for interop.

Comparisons

ConceptWhat it meansCan it have a value?Normal Kotlin useTypical example
UnitFunction completes normally with no meaningful resultYes, exactly one value: UnitVery commonfun log() { ... }
NothingFunction never completes normallyNoUsed for throw, infinite loops, fail-fast helpersfun fail(): Nothing { throw ... }
VoidJava interop type (java.lang.Void)Practically used as null when nullableRare in KotlinCallable<Void>

Unit vs Nothing

FeatureUnitNothing
Function can finish normallyYesNo
Has a valueYes (Unit)No
Used for side-effect functionsYesNo
Used for always-throwing functionsNoYes

Unit vs Void

FeatureUnitVoid
Idiomatic KotlinYesNo
Real Kotlin typeYesJava interop type
Common in pure Kotlin APIsYesRare
Often requires nullNoYes, when nullable

Quick rule

  • If your function just does work, use Unit.
  • If your function never returns, use Nothing.
  • If a Java API forces the type, use Void.
Take Quiz

Cheat Sheet

Quick reference

Unit

  • Means: no meaningful return value
  • Function completes normally
  • Kotlin's usual replacement for Java void
  • Has one value: Unit
  • Usually omitted in function declarations
fun printName() {
    println("Sam")
}

Nothing

  • Means: no value can ever be returned
  • Function never completes normally
  • Used for throw, fatal errors, infinite loops
  • Has no values
fun fail(): Nothing {
    throw IllegalStateException("Failed")
}

Void

  • Means: Java interop type java.lang.Void
  • Rare in Kotlin-first code
  • Mostly used in Java generic APIs
  • Often appears as Void? with null
val task: java.util.concurrent.Callable<Void> = java.util.concurrent.Callable {
    null
}

Rules to remember

  • Prefer Unit in Kotlin APIs.
  • Use Nothing when execution cannot continue.
  • Use Void only when Java libraries require it.
  • Unit is not null.
  • Nothing has no instances.
  • throw has type Nothing.

Common patterns

val name = input ?: throw IllegalArgumentException("Name required")
fun logEvent() = println("saved")
fun unreachable(): Nothing = throw IllegalStateException("Impossible state")

FAQ

What is the difference between Unit and Nothing in Kotlin?

Unit means a function finishes normally but has no meaningful result. Nothing means a function never finishes normally, such as one that always throws an exception.

Is Kotlin Unit the same as Java void?

They are similar in purpose, but not identical. Unit is a real type with one value, while Java void is not a normal type you can use as a value.

When should I use Void in Kotlin?

Usually only when working with Java APIs that explicitly require Void, especially generic types like Callable<Void>.

Why does throw work inside expressions in Kotlin?

Because throw has type Nothing. Kotlin knows that branch never returns, so it can fit into expressions expecting another type.

Can a function returning Nothing return null?

No. Nothing has no values at all, including null.

Should I write : Unit explicitly in Kotlin functions?

Usually no. Kotlin developers normally omit it unless writing it improves clarity.

Is Void the same as Unit in Kotlin?

No. Unit is the normal Kotlin type for no meaningful result. Void is mainly a Java interop type.

Can Nothing be useful with nullable values?

Yes. It is often used in Elvis expressions like value ?: throw ..., where the failure branch never returns.

Take Quiz

Mini Project

Description

Build a small Kotlin validation utility that demonstrates all three concepts: a normal action function using Unit, a fail-fast helper using Nothing, and a Java interop example using Void. This project mirrors real application code where you validate input, log progress, and sometimes connect to Java libraries.

Goal

Create a small program that validates a username, logs a success message, and includes a Java Callable<Void> task.

Requirements

  1. Create a function that prints a log message and returns normally.
  2. Create a function that always throws an exception for invalid input.
  3. Create a function that validates a nullable username using the Elvis operator.
  4. Add a Java Callable<Void> example that performs an action and returns null.
Take Quiz

Keep learning

Related questions

View more