# Extensions for Nullable Types

Sometimes it’s not what it looks like.

s?.f() implies that s is nullable—otherwise you could simply call s.f(). Similarly, t.f() seems to imply that t is non-nullable because Kotlin doesn’t require a safe call or programmatic check. However, t is not necessarily non-nullable.

The Kotlin standard library provides String extension functions, including:

  • isNullOrEmpty(): Tests whether the receiver String is null or empty.
  • isNullOrBlank(): Performs the same check as isNullOrEmpty() and allows the receiver String to consist solely of whitespace characters, including tabs (\t) and newlines (\n).

Here’s a basic test of these functions:

// NullableExtensions/StringIsNullOr.kt
import atomictest.eq

fun main() {
  val s1: String? = null
  s1.isNullOrEmpty() eq true
  s1.isNullOrBlank() eq true

  val s2 = ""
  s2.isNullOrEmpty() eq true
  s2.isNullOrBlank() eq true

  val s3: String = " \t\n"
  s3.isNullOrEmpty() eq false
  s3.isNullOrBlank() eq true
}

The function names suggest they are for nullable types. However, even though s1 is nullable, you can call isNullOrEmpty() or isNullOrBlank() without a safe call or explicit check. That’s because these are extension functions on the nullable type String?.

We can rewrite isNullOrEmpty() as a non-extension function that takes the nullable String s as a parameter:

// NullableExtensions/NullableParameter.kt
package nullableextensions
import atomictest.eq

fun isNullOrEmpty(s: String?): Boolean =
  s == null || s.isEmpty()

fun main() {
  isNullOrEmpty(null) eq true
  isNullOrEmpty("") eq true
}

Because s is nullable, we explicitly check for null or empty. The expression s == null || s.isEmpty() uses short-circuiting: if the first part of the expression is true, the rest of the expression is not evaluated, thus preventing a null pointer exception.

Extension functions use this to represent the receiver (the object of the type being extended). To make the receiver nullable, add ? to the type being extended:

// NullableExtensions/NullableExtension.kt
package nullableextensions
import atomictest.eq

fun String?.isNullOrEmpty(): Boolean =
  this == null || isEmpty()

fun main() {
  "".isNullOrEmpty() eq true
}

isNullOrEmpty() is more readable as an extension function.

  • -

Take care when using extensions for nullable types. They are great for simple cases like isNullOrEmpty() and isNullOrBlank(), especially with self-explanatory names that imply the receiver might be null. In general, it’s better to declare regular (non-nullable) extensions. Safe calls and explicit checks clarify the receiver’s nullability, while extensions for nullable types may conceal nullability and confuse the reader of your code (probably, “future you”).

Exercises and solutions can be found at www.AtomicKotlin.com.