Upgrade to Pro — share decks privately, control downloads, hide ads and more …

【Kotlin】【Android】Null安全でぬるぽに立ち向かう

 【Kotlin】【Android】Null安全でぬるぽに立ち向かう

2016.07.09 Kansai.kt #1 用資料です。KotlinのNull安全について調べたことをまとめています。

masanori_msl

July 09, 2016
Tweet

More Decks by masanori_msl

Other Decks in Programming

Transcript

  1. 【Kotlin】【Android】
    Null安全でぬるぽに立ち向かう
    2016.07.09 @Kansai.kt

    View Slide

  2. Who?
    Twitter: @masanori_msl
    vaguely: http://mslgt.hatenablog.com/
    SearchWakayamaToilet:
    https://play.google.com/store/apps/details?
    id=jp.searchwakayamatoilet
    Name: Masui Masanori

    View Slide

  3. はじめに
    Kotlinのいいところ

    View Slide

  4. はじめに
    Null安全

    View Slide

  5. はじめに
    Kotlinを使えば、
    ぬるぽ(NullPointerException)に
    サヨナラできる
    ∩( ・ω・)∩ばんじゃーい

    View Slide

  6. はじめに
    本当に?

    View Slide

  7. はじめに
    調べてみた

    View Slide

  8. Null許容(Nullable)
    Kotlinでは、変数をNullにすることが可能か
    そうでないかが区別される。
    var nullableText: String? = null
    ■Null許容型(Nullable Type)
    ■非Null許容型(Non-Null Type)
    var notNullableText: String = null // ※コンパイルエラー

    View Slide

  9. Null許容(Nullable)
    非Null許容型の変数に、Null許容型の変数を代入
    するには、後者がNullでないことを保証する必要
    がある。
    var nullableText: String? = "Not null"
    var notNullableText: String = nullableText!! // OK

    ではこれは...?
    var nullableText: String? = null
    var notNullableText: String = nullableText!!

    View Slide

  10. Null許容(Nullable)
    KotlinNullPointerExceptionが発生する

    View Slide

  11. Null許容(Nullable)
    Kotlinではlateinitを付けることで、変数の初期化
    のタイミングを変更できる。

    この変数を初期化せずに使うと ?
    lateinit var lateinitNotNullableText: String
    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    var notNullableText: String = lateinitNotNullableText
    var length = lateinitNotNullableText.length
    }

    View Slide

  12. Null許容(Nullable)
    UninitializedPropertyAccessExceptionが発生
    する
    lateinit var lateinitNotNullableText: String
    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    var notNullableText: String = lateinitNotNullableText // NG

    var length = lateinitNotNullableText.length // NG

    }

    View Slide

  13. Null許容(Nullable)
    Javaのコード上ではNull許容型・非Null許容型の
    制限が強制されない。
    MainActivity.kt
    NotNullClass.java
    - 省略 -
    public String getText(){
    return null; // 実行可能
    }
    - 省略 -
    val notNullableClass = NotNullableClass()
    val notNullableText: String = notNullableClass.getText()
    }

    View Slide

  14. Null許容(Nullable)
    IllegalArgumentExceptionが発生する
    Java側からKotlinのメソッドを呼ぶときの引数
    (非Null許容)にNullを渡そうとする場合は、
    @NonNullと同じく警告は表示されるが実行自体
    は可能。

    View Slide

  15. Kotlinのいいところ?
    Kotlinを使うだけで、
    ぬるぽにサヨナラできる

    View Slide

  16. Kotlinのいいところ?
    Kotlinを使うだけで、
    ぬるぽにサヨナラできる

    View Slide

  17. Kotlinのいいところ
    Kotlinを使うと、
    ぬるぽを発生させない設計が
    しやすくなる

    View Slide

  18. Kotlinのいいところ
    ΩΩΩ < ナ、 ナンダッテー!!

    View Slide

  19. Kotlinに力を借りて、
    ぬるぽに立ち向かう
    ここから本題

    View Slide

  20. Sample
    SearchWakayamaToilet
    https://github.com/masanori840816/SearchWakayamaToilet

    View Slide

  21. ぬるぽを無くすには?
    ぬるぽが嫌なら、
    コードからNullを
    無くしてしまえばいいじゃない

    View Slide

  22. 非Null許容型にする
    Nullにならないことを保証できる変数は、Nullを
    許容しないことでコードをシンプルにできる。
    class MainActivity : AppCompatActivity() {
    var notNullableText: String = "" // Nullを代入しない
    override fun onCreate(savedInstanceState: Bundle?) {
    - 省略 -
    var length = NotNullableText.length // Nullチェックが不要
    }
    }
    引数にも適用可能だが、特にコールバックで呼ば
    れた時に本当にNullが渡されないか確認が必要。

    View Slide

  23. lateinit
    Nullにならないことは確実だが、宣言時に値が入
    れられない変数ではlateinitを使う。
    class MainActivity : AppCompatActivity() {
    lateinit var notNullableClass: NotNullableClass // 後で初期化
    override fun onCreate(savedInstanceState: Bundle?) {
    - 省略 -
    // 宣言時に値が入れられない場合も非Null許容型にできる
    notNullableClass = NotNullableClass()
    }
    }

    View Slide

  24. 非Null許容型にする
    例えばMVCにおけるControllerなど、
    Nullにできない変数も非Null許容型にする。
    プログラム全体が動作しなくなる変数がNullの場
    合は、下手に延命するよりエラーなどでアプリを
    終了させる方が良い、ということ。

    View Slide

  25. Null許容型を使う
    コールバックメソッドの引数など、
    全部非Null許容型にするのは難しい。

           
     Null許容型を安全に使いたい

    View Slide

  26. Nullチェック
    class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
    - 省略 -
    var nullableText: String? = “Not null”
    var length = -1
    if(nullableText != null){ // Nullチェック
    // Nullをチェックしているので
    // ”!.”を使っても安全にlengthを実行可能
    length = nullableText!.length
    }
    }
    }
    通常のNullチェック

    View Slide

  27. Nullチェック
    class MainActivity : AppCompatActivity() {
     val nullableText: String? = “Not null”
    override fun onCreate(savedInstanceState: Bundle?) {
    - 省略 -
    var length = -1
    if(nullableText != null){ // Nullチェック
    // Nullで無いことが保証されるので
    // 非Null許容の変数と同じように扱うことが可能
    length = nullableText.length
    }
    }
    }
    valを使ってimmutableにできる場合

    View Slide

  28. Nullチェック
    class MainActivity : AppCompatActivity() {
     val nullableText: String? = “Not null”
    override fun onCreate(savedInstanceState: Bundle?) {
    - 省略 -
    var length = -1
    if(nullableText == null){ // Nullチェック
    return // Nullなら以下の処理をスキップ
    }
    // 非Null許容の変数と同じように扱うことが可能
    length = nullableText.length
    }
    }
    これもOK

    View Slide

  29. Nullチェック
    class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
    - 省略 -
    var nullableText: String? = “Not null”
    var length = -1
    if(nullableText != null){ // Nullチェック
    // Nullで無いことが保証されるので
    // 非Null許容の変数と同じように扱うことが可能
    length = nullableText.length
    }
    }
    }
    ローカル変数ならmutableな変数でもOK

    View Slide

  30. Nullチェック
    一度値を入れた後は値を変更しない変数
    メソッドに引数として渡される値
    ※引数は全てImmutable
    使いどころ

    View Slide

  31. ?演算子
    Null ”
    許容型の変数では、 ?.”を使うことで安全にア
    クセスできる。
    class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
    - 省略 -
    val nullableText: String? = “Not null”
    // nullableTextならNullが
    // それ以外はnullableText.lengthの値が入る
    var length = nullableText?.length
    }
    }

    View Slide

  32. ?演算子
    ”?.”を使ったチェーンメソッド
    class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
    - 省略 -
    val nullableText: String? = “Not null”
    // nullableText、lengthのどちらかがNullならNullが入る
    var lengthText = nullableText?.length?.toString()
    }
    }

    View Slide

  33. 疑問
    この2つのコード、結果は同じ。
    var nullableText: String? = “Not null”
    var nullableNum1: Int? = null
    var nullableNum2: Int? = null
    // Code 1
    if(nullableText != null){
    nullableNum1 = nullableText.length
    NullableNum2 = nullableText.length
    }
    // Code2
    nullableNum1 = nullableText?.length
    nullableNum2 = nullableText?.length
    実行速度に違いはある?

    View Slide

  34. 疑問
    調べてみた

    View Slide

  35. 計測
    class MainActivity : AppCompatActivity() {
    lateinit var logger: TimingLogger
    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    // ログの取得開始.
    logger = TimingLogger("KtTimingLogger", "Code")
    // getLengthOfCode1()またはgetLengthOfCode2()を実行
    // 測定を終了してログ出力する.
    logger.dumpToLog()
    }
    android.util.TimingLoggerを使って計測

    View Slide

  36. 計測
    fun getLengthOfCode1(){
    // Code1の検証.
    var nullableText: String? = "not null"
    var nullableNum1: Int? = null
    var nullableNum2: Int? = null
    for(i in 0..1000000) {
    if (nullableText != null) {
    nullableNum1 = nullableText.length
    nullableNum2 = nullableText.length
    }
    }
    logger.addSplit("Code1おしまい")
    }

    View Slide

  37. 計測
    fun getLengthOfCode2(){
    // Code2の検証.
    var nullableText: String? = "not null"
    var nullableNum1: Int? = null
    var nullableNum2: Int? = null
    for(i in 0..1000000) {
    nullableNum1 = nullableText?.length
    nullableNum2 = nullableText?.length
    }
    logger.addSplit("Code2おしまい")
    }

    View Slide

  38. 結果
    Code1 24ms
    Code2 24ms
    [検証環境]
    Android Studio ver. 2.1.2
    [端末]
    Nexus 9 (Emulator)
    [OS]
    Android N

    View Slide

  39. 結果
    ● “if(x != null)”でチェックする場合も?演算子を使
    う場合も、少なくとも今回の検証ではほぼ差が
    見られなかった。
    ● 状況に合わせてよりシンプルに書ける方を選択
    するのが良さそう。

    View Slide

  40. Elvis演算子
    class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
    - 省略 -
    var nullableText: String? = null
    // nullableTextがNull ”
    の場合のみ ?:”の右の値を代入する
    nullableText = nullableText?: “Null ”
    でした
    // 今回はローカル変数なので、もう一度nullableTextに
      // Nullチェック不要を入れるまではNullチェック不要
    }
    }
    ”?:”を使うことで、変数がNullの場合のみ値を代入
    できる。

    View Slide

  41. Elvis演算子
    class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
    - 省略 -
    var nullableText: String? = null
    // 変数がNullなら以降の処理をスキップ
    nullableText?: return
    - 省略 -
    }
    }
    ”?:”を使うと変数がNullの場合はreturnする、とい
    うことも可能。

    View Slide

  42. まとめ
    ● Kotlinはぬるぽを無くしてくれるわけではない。
    ● ぬるぽを無くすにはコールバックの引数などア
    プリの動きをより詳しく把握する必要がある。
    ● Kotlinの豊富な機能でハッピーな開発ライフを!

    View Slide

  43. 参考
    5.4 NULL 安全 - プログラミング言語Kotlin 解説
    16 日目:ぬるぽとの別れ - Kotlin Advent Calendar 2012 (全部俺)
    Reference - Kotlin Programming Language
    30分で覚えるKotlin 文法 - Qiita
    https://kotlinlang.org/docs/reference/
    https://sites.google.com/site/tarokotlin/chap5/sec54
    http://kotlin.hatenablog.jp/entry/2012/12/16/001635
    http://qiita.com/k5n/items/cc0377b75d8537ef8a85
    http://qiita.com/datsnet/items/1b868ced7c2fa8b4dcb0
    処理時間の計測をAndroid で簡単に行う - Qiita

    View Slide

  44. Credit
    Google Noto Fonts
     Main Page:
     License:
    Material icons
     Main Page:
     License:
    https://www.google.com/get/noto/
    SIL Open Font License (OFL)
    https://design.google.com/icons/
    CC-BY License

    View Slide

  45. Enjoy Kotlin Life :)

    View Slide