Skip to content

Instantly share code, notes, and snippets.

@ohtsuchi ohtsuchi/read_kotlin_chap12.md Secret
Last active Sep 29, 2017

Embed
What would you like to do?
Kotlinスタートブック(赤べこ本) 第12章 の 写経

Kotlinスタートブック(赤べこ本) 第12章 の 写経

第12章 Null 安全


1. Java における null

list 12.1 null 参照 の デリファレンス

// Java コード
package chap12;

class Main {
    public static void main(String[] args) {
        // list 12.1
        String s = null;
        s.toUpperCase(); // Exception in thread "main" java.lang.NullPointerException
    }
}

list 12.2 null か否か

// Java コード
package chap12;
import java.io.File;

class Main {
    public static void main(String[] args) {
        String str = "str";

        // list 12.2
        // 常に null でない例
        String upperCase = str.toUpperCase();
        // upperCase は null でない

        // 条件によって null になり得る例
        String parent1 = new File("/foo").getParent();
        String parent2 = new File("/").getParent();
        // parent1 は null でない
        // parent2 は null
    }
}

1.1 null か否か を区別する工夫

list 12.3 null か否か を区別する工夫

// Java コード

// アノテーション で null でないこと を表明
@Nonnull
String reverse(String str) { ... }

// アノテーション で null の可能性 を表明
@Nullable
User findUser(long id) { ... }

// 型で値の有無を表現
<T> Optional<T> first(List<T> list) { ... }

list 12.4 null は誰にも止められない!

// Java コード
@Nonnull
Optional<Foo> 絶対にnullを返さないメソッド() {
	return null;
}

2. Null 安全 という答え

// p174上
val s: String = null  // コンパイルエラー(Null can not be a value of a non-null type String)
fun main(args: Array<String>) {
    // p174下
    val s: String? = null
    println(s)       // null
    // p175
    s.toUpperCase()  // コンパイルエラー (Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String?)
}

3. Smart Cast

  • 条件分岐 によるチェック

list 12.5 NotNull への Smart Cast

fun main(args: Array<String>) {
    // list 12.5
    val a: String? = null
    val b: String? = "Hello"

    if (a != null) {
        println(a.toUpperCase())
    }
    if (b != null) {
        println(b.toUpperCase()) // HELLO
    }
}

list 12.6 Smart Cast の例

  • is 型 -> ...
fun main(args: Array<String>) {
    // list 12.6
    val list: List<Any> = listOf(1, 'a', false)
    for (e in list) {
        val result: Any? = when (e) {
            is Int -> e + 5            // e は Int 型
            is Char -> e.toUpperCase() // e は Char 型
            is Boolean -> e.not()      // e は Boolean 型
            else -> null
        }
        println(result)
    }
}
  • 実行結果
6
A
true

4. 安全呼び出し

list 12.7 null なら null を返すだけ

fun main(args: Array<String>) {
    // list 12.7
    val a: Int? = 5
    val aInc =
            if (a != null) a.inc()
            else null

    println(aInc) // 6
}

list 12.8 安全呼び出し

  • ?.
fun main(args: Array<String>) {
    val a: Int? = 5
    //
    // list 12.8
    //
    val aInc = a?.inc()  // aInc は `Int? 型`

    println(aInc) // 6
}

list 12.9 NotNull な 引数 を取る関数 に Nullable を渡す

fun square(i: Int): Int = i * i

fun main(args: Array<String>) {
    val a: Int? = 5
    val aSquare =
            if (a != null) square(a)
            else null

    println(aSquare)        // 25  (`aSquare` は Int? 型)
}

list 12.10 拡張関数 let の 定義

public inline fun <T, R> T.let(block: (T) -> R): R = block(this)

list 12.11 let + 安全呼び出し

fun square(i: Int): Int = i * i

fun main(args: Array<String>) {
    val a: Int? = 5

    // list 12.11 `let` + 安全呼び出し
    val aSquare = a?.let { square(it) }
    //            if (a != null) square(a)
    //            else null
    println(aSquare)        // 25  (`aSquare` は Int? 型)

    // p179下 の説明: `square` の 関数object を渡す
    val aSquare2 = a?.let(::square)
    println(aSquare2)       // 25  (`aSquare2` は Int? 型)

    // 以下, 追記
    // 1引数 の lambda expression (引数名 を明示的に指定)
    val aSquare3 = a?.let { i -> square(i) }
    println(aSquare3)       // 25  (`aSquare3` は Int? 型)
}

5. !! 演算子

  • Nullable -> NotNull に 強制変換
fun main(args: Array<String>) {
    // p180上
    val foo: String? = "Hello"
    val bar: String = foo!!
    println(bar.toUpperCase()) // HELLO
}
  • null に対して -> 例外
fun main(args: Array<String>) {
    // p180中
    val hoge: String? = null
    val fuga: String = hoge!! // kotlin.KotlinNullPointerException
}
  • !! は 危険 -> requireNotNull を使用しましょう
fun main(args: Array<String>) {
    // p180下
    val foo: String? = "Hello"
    val bar: String = requireNotNull(foo, { "null なわけがない" })
    println(bar.toUpperCase()) // HELLO

    val hoge: String? = null
    val fuga: String = requireNotNull(hoge, { "hoge は null であってはダメ" })
    // java.lang.IllegalArgumentException: hoge は null であってはダメ
}

6. Elvis Operator (エルビス演算子)

fun main(args: Array<String>) {
    // p181上
    val foo: String? = "Hello"
    val a = (if (foo != null) foo else "default").toUpperCase()
    println(a)  // HELLO

    val hoge: String? = null
    val b = (if (hoge != null) hoge else "default")
    println(b)  // default
}
  • NotNull = Nullable ?: デフォルト値
fun main(args: Array<String>) {
    // p181下
    val foo: String? = "Hello"
    val a = (foo ?: "default").toUpperCase() // a は `String`
    println(a)  // HELLO

    val hoge: String? = null
    val b = hoge ?: "default"                // b は `String`
    println(b)  // default
}
  • NotNull = Nullable ?: (Nullable -> NotNull に 変換できない場合 に throw する 例外)
fun main(args: Array<String>) {
    // p182
    val foo: String? = "Hello"
    val a = foo ?: throw AssertionError() // a は `String`
    println(a)  // Hello

    val hoge: String? = null
    val b = hoge ?: throw AssertionError() // b は `String`
    // java.lang.AssertionError
}

7. Safe Cast (安全キャスト)

  • as?
    • Cast 失敗した場合: null
fun main(args: Array<String>) {
    // p183
    val str: Any = "本当は文字列"
    val s = str as String
    println(s)  // 本当は文字列

    // 実行時エラー
    str as Int  // java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer

    val i = str as? Int // `as?` (安全 cast)
    println(i)  // null
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.