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") } ```