Question
What does a single exclamation mark (!) mean in Kotlin?
I have seen it in Kotlin code a few times, especially when using Java APIs. I could not find a clear explanation in the documentation or on Stack Overflow. What does it do, and why does it appear in those situations?
Short Answer
By the end of this page, you will understand that a single exclamation mark (!) in Kotlin usually means boolean negation: it flips true to false and false to true.
You will also learn why this symbol often comes up when working with Java APIs, how it differs from Kotlin's !! operator, and how to read and write common real-world examples correctly.
Concept
In Kotlin, a single exclamation mark (!) is the logical NOT operator.
It is used with boolean expressions to reverse their value:
!truebecomesfalse!falsebecomestrue
Why this matters
Boolean logic is everywhere in programming:
- checking whether a value is valid
- reversing a condition
- handling permission or feature flags
- filtering data
- deciding whether to enter an
ifblock
For example:
val isLoggedIn = false
println(!isLoggedIn) // true
Here, isLoggedIn is false, so !isLoggedIn becomes true.
Why you may notice it more with Java APIs
When calling Java code from Kotlin, you often deal with methods like:
isEmpty()exists()hasNext()isValid()
Sometimes you want the opposite meaning, so you negate the result:
if (!file.exists()) {
println("File not found")
}
The exists() method returns a boolean. Adding ! means "if the file does not exist".
Important: ! is not the same as !!
A very common source of confusion is mixing up:
!→ logical NOT for booleans!!→ non-null assertion operator
Example:
val isReady = true
println(!isReady) // false
Versus:
val name: String? = "Kotlin"
println(name!!) // Kotlin
name!! is about nullability, not boolean negation.
A note about Java platform types
When working with Java APIs, Kotlin may infer some values as platform types. This can make code involving null checks or boolean logic look unfamiliar. But the meaning of a single ! is still the same: it negates a boolean expression.
Mental Model
Think of ! as a switch that flips a yes/no answer.
truemeans yesfalsemeans no!flips the answer
So:
isOpen = true→ the door is open!isOpen→ "the door is not open"
Another way to think about it:
isAvailable()asks a question!isAvailable()means "the answer to that question is no"
It is similar to saying:
happy→ happy!happy→ not happy
This is why ! reads naturally in conditions:
if (!user.isActive) {
println("User is inactive")
}
Read it as: if not active.
Syntax and Examples
The basic syntax is simple:
!expression
The expression must evaluate to a Boolean.
Basic example
val isSunny = true
println(!isSunny) // false
Because isSunny is true, negating it gives false.
Using ! in an if statement
val hasPermission = false
if (!hasPermission) {
println("Access denied")
}
This runs because hasPermission is false, so !hasPermission becomes true.
Common Java API style example
import java.io.File
val file = File("data.txt")
if (!file.exists()) {
println("The file does not exist")
}
file.exists() returns a boolean. !file.exists() means the opposite.
Negating comparisons
val age = 15
println(!(age >= 18)) // true
The parentheses matter here. First, age >= 18 is evaluated. Then ! negates the result.
Invalid usage
val number = 5
// println(!number) // Error
You cannot use ! directly on non-boolean values.
Related readable alternative
Sometimes instead of writing:
if (!items.isEmpty()) {
println("There are items")
}
it is clearer to write:
if (items.isNotEmpty()) {
println("There are items")
}
Both are valid, but the second is often easier to read.
Step by Step Execution
Consider this example:
val isEmpty = false
if (!isEmpty) {
println("Container has data")
}
Here is what happens step by step:
isEmptyis assigned the valuefalse.- Kotlin evaluates the condition
!isEmpty. - Since
isEmptyisfalse,!isEmptybecomestrue. - The
ifcondition is therefore true. - Kotlin executes the code inside the block.
- The output is:
Container has data
Another trace with a Java-style method
import java.io.File
val file = File("report.txt")
if (!file.exists()) {
println("Missing file")
}
Step by step:
- A
Fileobject is created forreport.txt. - Kotlin calls
file.exists(). - Suppose
exists()returnsfalse. !falsebecomestrue.- The
ifblock runs. Missing fileis printed.
This is the most common pattern you will see: call a boolean-returning method, then negate it if you need the opposite condition.
Real World Use Cases
! is used constantly in everyday Kotlin code.
Validation
if (!email.contains("@")) {
println("Invalid email")
}
File and resource checks
if (!configFile.exists()) {
println("Configuration file is missing")
}
Feature flags
if (!featureEnabled) {
println("Feature is disabled")
}
Loop control
while (!queue.isEmpty()) {
println(queue.removeFirst())
}
API or database result checks
if (!results.isEmpty()) {
println("Show results")
}
Authentication and permissions
if (!user.isAuthenticated) {
println("Please sign in")
}
In all of these examples, the code asks a yes/no question and ! flips the answer.
Real Codebase Usage
In real projects, developers often use ! in a few common patterns.
Guard clauses
A guard clause exits early when something is not valid.
fun process(user: User?) {
if (user == null || !user.isActive) return
println("Processing ${user.name}")
}
Here, !user.isActive stops processing inactive users.
Validation checks
fun save(input: String) {
if (!input.isNotBlank()) {
throw IllegalArgumentException("Input cannot be blank")
}
}
This works, though input.isBlank() would usually be clearer.
Error handling
if (!response.isSuccessful) {
println("Request failed")
}
Filtering logic
val activeUsers = users.filter { !it.isBanned }
This keeps users whose isBanned property is false.
Working with Java libraries
Java libraries often expose boolean methods with names like:
isVisible()hasMoreElements()canRead()exists()
Kotlin developers negate these when needed:
if (!file.canRead()) {
println("Cannot read file")
}
Readability tip
In production code, many teams prefer naming booleans positively so the condition reads well:
if (!isEnabled) { ... }
is often easier to understand than:
if (isDisabled) { ... }
But if too many negations pile up, readability suffers.
Common Mistakes
Beginners often confuse ! with a few other Kotlin features.
Mistake 1: Confusing ! with !!
Broken understanding:
val name: String? = "Ada"
println(name!!)
This is not boolean negation. It is the non-null assertion operator.
!flips a boolean!!tells Kotlin to treat a nullable value as non-null
Mistake 2: Using ! on a non-boolean value
Broken code:
val number = 10
// println(!number)
Why it fails:
numberis anInt, not aBoolean!only works on boolean expressions
Correct approach:
println(!(number > 5))
Mistake 3: Forgetting parentheses in more complex expressions
Potentially confusing code:
val age = 20
println(!age.equals(18))
This is valid, but often less readable than:
println(!(age == 18))
For comparisons, parentheses help make the order clear.
Mistake 4: Creating double negatives
Hard-to-read code:
if (!isNotReady) {
println("Ready")
}
This works, but it is mentally harder to parse.
Better:
if (isReady) {
println("Ready")
}
Mistake 5: Using negation when Kotlin has a clearer function
Less clear:
if (!items.isEmpty()) {
println("Has items")
}
Clearer:
if (items.isNotEmpty()) {
println("Has items")
}
The result is the same, but readability improves.
Comparisons
Here are a few related Kotlin operators and patterns that are often confused with !.
| Syntax | Meaning | Example | Notes |
|---|---|---|---|
!x | Logical NOT | !isReady | Flips a boolean value |
x && y | Logical AND | isLoggedIn && isAdmin | True only if both are true |
| `x | y` | Logical OR | |
!!x | Non-null assertion | name!! | Throws if name is null |
x == null | Null check | user == null | Checks whether a value is null |
x != null | Not-null check | user != null | Common before using a nullable value |
! vs !!
This is the most important comparison.
val isReady = false
println(!isReady) // true
val text: String? = "Hello"
println(text!!) // Hello
!is for booleans!!is for nullable references
!isEmpty() vs isNotEmpty()
| Form | Works? | Readability |
|---|---|---|
!items.isEmpty() | Yes | Acceptable |
items.isNotEmpty() | Yes | Usually clearer |
Kotlin often provides positive helper methods that reduce the need for negation.
Cheat Sheet
! in Kotlin quick reference:
Core rule
!booleanExpression
- Reverses a
Boolean !true→false!false→true
Common examples
val isOpen = false
println(!isOpen) // true
if (!file.exists()) {
println("Missing file")
}
if (!(age >= 18)) {
println("Minor")
}
Important distinctions
!x // boolean negation
x!! // non-null assertion
Works with
- boolean variables
- boolean-returning functions
- boolean comparisons
Does not work with
IntString- arbitrary objects
unless you first convert them into a boolean expression
Readability tips
Prefer clearer alternatives when available:
items.isNotEmpty()instead of!items.isEmpty()text.isBlank()instead of!text.isNotBlank()
Avoid double negatives such as:
!isNotReady
FAQ
Is a single ! in Kotlin the same as Java?
Yes. In both languages, a single ! means logical NOT and negates a boolean expression.
Why do I see ! often when using Java APIs in Kotlin?
Because many Java methods return booleans, such as exists(), isEmpty(), or hasNext(). Kotlin uses ! to express the opposite condition.
Is ! related to null safety in Kotlin?
Not directly. ! is for boolean negation. !! is the operator related to nullability.
Can I use ! on nullable booleans?
Only if you handle null safely first. For example:
val flag: Boolean? = null
println(flag == false)
Using !flag directly is not valid when flag is nullable.
What is the difference between !items.isEmpty() and items.isNotEmpty()?
They mean the same thing, but items.isNotEmpty() is usually easier to read.
Does ! work on numbers in Kotlin?
No. It only works on boolean expressions.
Why was it hard to find in documentation?
Because ! is a basic operator, it is usually documented as part of Kotlin operators and expressions rather than as a separate language feature page.
Mini Project
Description
Build a small Kotlin console program that checks whether a file path is usable. This project demonstrates how ! is used with boolean-returning methods from Java APIs such as exists(), canRead(), and isDirectory(). It also shows how negated conditions help with validation and early error messages.
Goal
Create a program that reports whether a given file exists, is readable, and is not a directory.
Requirements
- Accept or define a file path in the program.
- Check whether the file exists.
- Check whether the path is readable.
- Check whether the path points to a directory.
- Print a clear message for each failed check.
- Print a success message only when all checks pass.
Keep learning
Related questions
Accessing Kotlin Extension Functions from Java
Learn how Kotlin extension functions are compiled and how to call them correctly from Java with clear examples and common pitfalls.
Add a Maven Repository URL in Gradle Kotlin DSL (build.gradle.kts)
Learn how to add Maven Central and a custom Maven repository URL in Gradle Kotlin DSL using build.gradle.kts syntax.
Allow HTTP and HTTPS in Android 9 Pie with Network Security Configuration
Learn how Android 9 Pie handles cleartext HTTP traffic and how to allow HTTP and HTTPS safely using network security config.