Algus

Null Safety and Exceptions

[[ Null ]] ⇣

Kotlin prevents you from assigning a null value to a variable of a non-nullable type. If you want a value to be nullable, you must explicitly opt-in.

To mark the type as nullable, place a question mark after the name of the type, like String?.

How to work with the possibility of null

Checking for null with if/else statement

if(quest != null){
 // Safe to use `quest`
 println(quest.showQuest())
}

Using the safe call operator (?.)

val theQuest = quest?.showQuest()
if(theQuest != null){
	println(theQuest)
}

// Using `let`
val theQuest = quest?.showQuest()
theQuest.let {
	println(theQuest)
}

// using `it` default param
quest?.showQuest()?.let { println(it) } 
// same as => .let { theQuest -> println(theQuest) }

Using non-null assertion operator (!!)

There are situations where using the double-bang operator is appropriate. Perhaps you do not have control over the type of a variable, but you are sure that it will never be null. As long as you are confident that the variable you are using will not be null when you use it, then !! is an option.

val playerLevel = readLine()? // using safe call operator
	.toIntOrNull() // casting the string from stdin
	?: 0 // Using the Elvis Operator
 // ^ If left hand is null, use the right hand

[[ Exceptions ]] ⇣

Throwing an exception

Kotlin allows you to manually signal that an exception has occurred.

private fun obtainQuest(
 // ...
): String? {
	if (playerLevel <= 0) {
		throw IllegalArgumentException(
			"level must be at least 1"
		)
	}
	// ..
}

Handling exceptions

Kotlin allows handling exceptions by the usage of try/catch.

try {
    // some code
} catch (e: Exception) {
    // handler
} finally {
    // optional finally block
}

// `try` is an expression
val a: Int? = try {
		input.toInt() 
	} catch (e: NumberFormatException) {
		null 
	}

Preconditions

These functions allow us to define preconditions that must be true before executing some code.

// before:
if (playerLevel <= 0) {
	throw IllegalArgumentException(
		"level must be at least 1"
	)
}

// using the `require` precondition function
require(playerLevel > 0) { // condition
	"level must be at least 1"
	// ^ Throws an `IllegalArgumentException` if the condition is false.
}
FunctionDescription
checkThrows an IllegalStateException if the argument is false.
checkNotNullThrows an IllegalStateException if the argument is null. Otherwise returns the non-null value.
requireThrows an IllegalArgumentException if the argument is false
requireNotNullThrows an IllegalArgumentException if the argument is null, Otherwise returns the non-null value.
errorThrows an IllegalArgumentException with a provided message if the argument is null. Otherwise returns the non-value.
assertThrows an AssertionError if the argument is false and the assertion compiler flag is enabled

💡 Prefer IllegalArgumentException when you are checking input to a function and IllegalStateException for most other scenarios.

Defining a custom exception

class InvalidPlayerLevelException() :
	IllegalArgumentException("Invalid player level")

// throw the custom exception
throw InvalidPlayerLevelException()