Roughly cover the materials in Chapter 6: Null Safety and Exceptions.
## `null`
+ A special value means "Does not exist", "No such data", "Operation failed", "A special state", "not repersentable", or similar concept.
+ In many languages, `null` causes crashes!
+ Including C/C++/Java.
+ You may not open a file "does not exist".
+ You may not do arithmetic operation on "No such data".
+ `1 + null`
+ `"abc" + null`
+ Member function call
+ Almost all Java data types may have `null` values.
+ In Kotlin, you need special declaration to assign `null` to a variable or a value.
+ Nullability
+ Nullable: can be assigned to `null`
+ Type with `?` mark
+ `var x: Int? = null`
+ Non-nullable: cannot be assigned to `null`
+ Type without `?` mark
+ `var x: Int = 5`
+ Almost all types of Java are nullable.
+ "Check before use" make your code much longer and hard to read.
+ Kotlin does not allow to assign a value of a nullable type to a variable of the corresponding non-nullable type.
+ Safer: less unexpected crashes
## Null Safety
+ For compatibility, Kotlin limits the usage of nullable variables.
+ Convert nullable type values to non-nullable values
+ Safe (member function) calls
+ A function returns nullable type: [`readLine()`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io/read-line.html)
+ Read a line from the standard input.
+ Return type: `String?`
+ It is in `kotlin-stdlib`, so you don't need to `import` anything.
+ The following code does not work:
```kotlin
fun main() {
println("Input your name:")
val name = readLine()
println("Hello, $name!")
println("Your name has ${name.count()} characters")
}
```
+ `name` can be `null`: Press `ctrl` + `d` to terminate the input.
+ `name.count()` is an unsafe call!
+ In Kotlin, there are several ways to convert nullable type to non-nullable type.
+ Non-null Assert: Double-bang operator `!!`
+ Works on nullable variables / values
+ If the value is `null`, then this operator crash the program with an "Exception"
+ Use `!!` when you are **extremely confident** that the value is not `null`.
+ Two ways to use `!!` to fix the above sample code:
+ Use `readLine()!!`
+ Use `name!!.count()`
+ Note: does not solve the problem if the user terminates the standard input immediately.
+ Use `if` and `!= null` to assign a "default value" when `readLine()` returns `null`.
```kotlin
fun main() {
println("Input your name:")
val buf = readLine()
val name = if (buf != null) buf else ""
println("Hello, $name!")
println("Your name has ${name.count()} characters")
}
```
+ Null coalescing: Elvis operator `?:`
+ Try ```val name = readLine() ?: ""```
+ If the value on the left is `null`, then the result is the value on the right.
+ If the value on the left is not `null`, then the result is the value on the left.
+ Will not crash
+ Safe calls `?.`: call the member function only when the value is not `null`.
+ Consider a `String?`-type variable `str`
+ If `str` is not `null`:
+ `str?.count()` is the number of characters in `str`
+ Function `count()` is called.
+ If `str` is `null`:
+ `str?.count()` is `null`.
+ Function `count()` is not called.
+ `str?.count()` has type `Int?`.
## Why `null`?
+ To provide a special state without crash immediately.
+ `readLine()` returns `null` when end-of-file reached.
+ As an initial value: "Not initialized yet"
+ Sometimes, the initialization costs much
+ Kotlin provides tools to prevent problems with `null`.
## Exception
+ When something went wrong, we cannot properly execute the code, then we "throw" an `Exception`.
+ `readLine()!!` throws `NullPointerException` when end-of-file reached.
+ `1/0` throws `ArithmeticException` with message `/ by zero`
+ Unhandled Exception will crash your program.
+ Handled Exception can change workflow.
### Throwing Exception
+ Basic usage:
```kotlin
throw Exception("Error message")
```
+ Sample code:
```kotlin
fun main() {
println("What is your name?")
val name = readLine() ?: throw Exception("End-Of-File is reached.")
println("Hello, $name!")
}
```
### Custom Exception
+ Will be discuss later. We need the concept of `class` to define custom data objects (including `Exception`).
### Exception Handling
+ In Kotlin, we use `try`/`catch` statement to intercept `Exception`s to prevent the crash.
```kotlin
try{
statements...
} catch (e1: ExceptionType1) {
// If any ExceptionType1 thrown in the try-block
// It will be caught and run the catch-block
statements...
} catch (e2: ExceptionType2) {
...
...
} catch (eN: ExceptionTypeN) {
// If any ExceptionTypeN thrown in the try-block
// It will be caught and run the catch-block
statements...
}
// If the thrown exception is not ExceptionType1,
// ExceptionType2, ..., ExceptionTypeN, then
// throw it the the caller of the function
```
+ We can only catch certain kind of `Exception`.
+ The exception thrown in a function `myFun`
+ If the exception is not caught by any `try`/`catch` will terminate that `myFun`.
+ The caller of `myFun` will throw the exception again.
+ If at some stage, a `try`/`catch` handles the exception, then the program will not crash.
+ If the exception is thrown to `main` and no `try`/`catch` there, then the program will crash.
```kotlin
fun myFun(): Nothing {
println("Input p:")
val p = readLine()?.toInt()
println("Input q:")
val q = readLine()?.toInt()
println(p!! / q!!)
throw Exception("No exception caught.")
}
fun main() {
try{
myFun()
}
catch (npe: NullPointerException) {
println("An NPE is detected: ${npe}")
} catch (ae: ArithmeticException) {
println("An AE is detected: ${ae.message}")
}
// An uncaught Exception will crash the program
}
```
## Return Type: Nothing
+ A function return `Nothing` is a function that always throws an exception.
+ Example: [`TODO`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-t-o-d-o.html)
### Preconditions
+ [`error`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/error.html)
+ [`check`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/check.html)
+ [`require`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/require.html)
+ [`assert`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/assert.html)
+ [Reference](https://www.bignerdranch.com/blog/write-better-code-using-kotlins-require-check-and-assert/)
```kotlin
import java.util.Scanner
private fun scoreToGPA(score: Int): Double {
require(0 <= score && score <= 100) {
"$score is not a valid score in NYCU."
}
val yourGPA = when (score) {
in 90..100 -> 4.3
in 85..89 -> 4.0
in 80..84 -> 3.7
in 77..79 -> 3.3
in 73..76 -> 3.0
in 70..72 -> 2.7
in 67..69 -> 2.3
in 63..66 -> 2.0
in 60..62 -> 1.7
in 50..59 -> 1.0
else -> 0.0
}
check(0.0 <= yourGPA && yourGPA <= 4.3)
return yourGPA
}
fun main() {
val scanner = Scanner(System.`in`)
print("What is your score? ")
val yourScore = scanner.nextInt()
val yourGPA = scoreToGPA(yourScore)
assert(1.0 <= yourGPA && yourGPA <= 4.3)
print("Your GPA is $yourGPA")
}
```