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

違いがワカルKotlinプログラマーへの道

Swimmy
July 13, 2024
120

 違いがワカルKotlinプログラマーへの道

Swimmy

July 13, 2024
Tweet

More Decks by Swimmy

Transcript

  1. 登場人物 late init var / by lazy スコープ関数 == /

    === in / out enum / sealed class sealed intarface / class Any / Nothing / <T> data class / value class inner class / class ReadOnlyProperty const val / private val
  2. 登場人物 late init var / by lazy スコープ関数 == /

    === in / out enum / sealed class sealed intarface / class Any / Nothing / <T> data class / value class inner class / class ReadOnlyProperty 使い分けに悩む言語表現がたくさんある
  3. スコープ関数 頻出度 ★★★★★ apply / let / also /run /

    withなどラムダの範囲内で 指定されたオブジェクトを操作できるようになる構文 5種類もあり使い分けがよく分からないが 簡単に言うとラムダ内で受ける値と戻り値に違いがある (以降、独断と偏見の可能性あり) 各々ある程度自由に使っているのではないかなと思ってます よく見る形をベースに使い分けを紹介します
  4. スコープ関数 let よく見る形) (オブジェクト)?.let { (nullでない時の処理を返す) } ?: (null時の処理を返す) 例)

    val name: String? = null // ... val result = name?.let { processA(it) } ?: DEFAULT_NAME 以下同文) val result = if(name != null) { processA(name) } else { DEFAULT_NAME }
  5. スコープ関数 let よく見る形) (オブジェクト)?.let { (nullでない時の処理を返す) } ?: (null時の処理を返す) 例)

    val name: String? = null // ... val result = name?.let { processA(it) } ?: DEFAULT_NAME 以下同文) val result = if(name != null) { processA(name) } else { DEFAULT_NAME }
  6. スコープ関数 let よく見る形) (オブジェクト)?.let { (nullでない時の処理を返す) } ?: (null時の処理を返す) 例)

    val name: String? = null // ... val result = name?.let { processA(it) } ?: DEFAULT_NAME 以下同文) val result = if(name != null) { processA(name) } else { DEFAULT_NAME }
  7. スコープ関数 apply よく見る形) (オブジェクト).apply { (何かを設定する) } ※ 戻り値はオブジェクト 例)

    return Swimmy().apply { favoriteFood = "hamburger" } 以下同文) val swimmy = Swimmy() swimmy.favoriteFoold = "hamburger" return swimmy
  8. スコープ関数 apply よく見る形) (オブジェクト).apply { (何かを設定する) } ※ 戻り値はオブジェクト 例)

    return Swimmy().apply { favoriteFood = "hamburger" } 以下同文) val swimmy = Swimmy() swimmy.favoriteFoold = "hamburger" return swimmy
  9. スコープ関数 apply よく見る形) (オブジェクト).apply { (何かを設定する) } ※ 戻り値はオブジェクト 例)

    return Swimmy().apply { favoriteFood = "hamburger" } 以下同文) val swimmy = Swimmy() swimmy.favoriteFoold = "hamburger" return swimmy
  10. スコープ関数 apply よく見る形) (オブジェクト).apply { (何かを設定する) } ※ 戻り値はオブジェクト 例)

    return Swimmy().apply { favoriteFood = "hamburger" } 以下同文) val swimmy = Swimmy() swimmy.favoriteFoold = "hamburger" return swimmy 大体ここまでで事足りる気がする
  11. スコープ関数 also よく見る形) (非同期処理).onSuccess {...}.onFailure{...}.also { (最後に何かする) } ※ applyとほぼ同じ・・

    ※ 戻り値はオブジェクト 例 : 個人的な偏見) runCathing { repository.getSampleData() } .onSuccess { ... } .onFailure { ... } .also { finishLoading() }
  12. スコープ関数 also よく見る形) (非同期処理).onSuccess {...}.onFailure{...}.also { (最後に何かする) } ※ applyとほぼ同じ・・

    ※ 戻り値はオブジェクト 例 : 個人的な偏見) runCathing { repository.getSampleData() } .onSuccess { ... } .onFailure { ... } .also { finishLoading() }
  13. スコープ関数 run よく見る形) (オブジェクト)?.run { (nullじゃない時いっぱい何かする) } ?: (nullだった時の処理) 例)

    val name: String? = null // ... name?.run { processA() ...} ?: processB() 以下同文) if(name != null) { processA() } else { processB() }
  14. スコープ関数 run よく見る形) (オブジェクト)?.run { (nullじゃない時いっぱい何かする) } ?: (nullだった時の処理) 例)

    val name: String? = null // ... name?.run { processA() ...} ?: processB() 以下同文) if(name != null) { processA() } else { processB() }
  15. スコープ関数 run よく見る形) (オブジェクト)?.run { (nullじゃない時いっぱい何かする) } ?: (nullだった時の処理) 例)

    val name: String? = null // ... name?.run { processA() ...} ?: processB() 以下同文) if(name != null) { processA() } else { processB() }
  16. スコープ関数 run よく見る形) (オブジェクト)?.run { (nullじゃない時いっぱい何かする) } ?: (nullだった時の処理) 例)

    val name: String? = null // ... name?.run { processA() ...} ?: processB() 以下同文) if(name != null) { processA() } else { processB() } withのnullable考慮版
  17. 普通のクラスとの主な違い class Normal data class Model(val name: String) // プロパティは必須

    val normal = Normal() val model = Model("swimmy") normal.copy() // compile error... model.copy(name = "new name") // 状態更新の時に使える normal.component1() // compile error... model.component1() // 1個目のプロパティである「new name」が出力される
  18. 普通のクラスとの主な違い class Normal data class Model(val name: String) // プロパティは必須

    val normal = Normal() val model = Model("swimmy") normal.copy() // compile error... model.copy(name = "new name") // 状態更新の時に使える normal.component1() // compile error... model.component1() // 1個目のプロパティである「new name」が出力される
  19. 普通のクラスとの主な違い class Normal data class Model(val name: String) // プロパティは必須

    val normal = Normal() val model = Model("swimmy") normal.copy() // compile error... model.copy(name = "new name") // 状態更新の時に使える normal.component1() // compile error... model.component1() // 1個目のプロパティである「new name」が出力される
  20. 普通のクラスとの主な違い class Normal data class Model(val name: String) // プロパティは必須

    val normal = Normal() val model = Model("swimmy") normal.toString() // Normal@xxxx ハッシュ値を含んだものが出力 model.toString() // Model(name = swimmy)
  21. 普通のクラスとの主な違い class Normal data class Model(val name: String) // プロパティは必須

    val normal = Normal() val normal2 = Normal() val model = Model("swimmy") val model2 = Model("swimmy") normal.equal(normal2) // false 別インスタンスなので当然 model.equal(model2) // true データが同じなので同じという判定
  22. const val / private val 頻出度 ★★★★☆ companion object内で private

    const val XXX = "DEFAULT" を良く見るがconstってなんのために付与している? -> 実行速度を上げるため constをつけた値はコンパイル時に値が変換されるので javaのgetterを介さずに直接アクセスできる
  23. enumとの違い // 例外がある場合 enum Sports(val item: String?) { Tennis("racket"), Baseball("bat"),

    Soccer("ball"), Run(null), } // スマートにできる sealed interface Sports { data class Tennis(val item: String = "racket") : Sports data class Baseball(val item: String = "bat") : Sports data class Soccer(val item: String = "ball") : Sports data object Run : Sports }
  24. enumとの違い // 例外がある場合 enum Sports(val item: String?) { Tennis("racket"), Baseball("bat"),

    Soccer("ball"), Run(null), } // スマートにできる sealed interface Sports { data class Tennis(val item: String = "racket") : Sports data class Baseball(val item: String = "bat") : Sports data class Soccer(val item: String = "ball") : Sports data object Run : Sports }
  25. enumとの違い // ネストができない enum Sports(...) { enum BallGame(...) // NG

    Run(...) } // ネストが可能 sealed interface Sports { sealed interface BallGame { ... } data object Run: Sports }
  26. enumとの違い // ネストができない enum Sports(...) { enum BallGame(...) // NG

    Run(...) } // ネストが可能 sealed interface Sports { sealed interface BallGame { ... } data object Run: Sports }
  27. in / out 頻出度 ★☆☆☆☆ 内部コードや独自のクラス、拡張関数で見るけど謎だよね Kotlinの型安全を実現する目的で使われる out(出力時のみ使用可能) サブクラス is

    スーパークラスのサブタイプ関係が成立する in(引数で使用可能) スーパークラス is サブクラスのサブタイプ関係が成立する
  28. in / out class Swimmy<T>(val speed: T) val swimmy: Swimmy<Int>

    = Swimmy(100) val swimmy2: Swimmy<Number> = swimmy // compile error -------------------------------------------------------------- class Swimmy<out T>(val speed: T) val swimmy: Swimmy<Int> = Swimmy(100) val swimmy2: Swimmy<out Number> = swimmy // OK
  29. in / out class Swimmy<T>(val speed: T) val swimmy: Swimmy<Int>

    = Swimmy(100) val swimmy2: Swimmy<Number> = swimmy // compile error -------------------------------------------------------------- class Swimmy<out T>(val speed: T) val swimmy: Swimmy<Int> = Swimmy(100) val swimmy2: Swimmy<out Number> = swimmy // OK
  30. in / out class Swimmy<T>(val speed: T) val swimmy: Swimmy<Int>

    = Swimmy(100) val swimmy2: Swimmy<Number> = swimmy // compile error -------------------------------------------------------------- class Swimmy<out T>(val speed: T) val swimmy: Swimmy<Int> = Swimmy(100) val swimmy2: Swimmy<out Number> = swimmy // OK public interface List<out E> : Collection<E>
  31. in / out open class Swimmy<T>(val speed: T) val swimmy:

    Swimmy<Int> = Swimmy(100) val swimmy2: Swimmy<Number> = swimmy // compile error val swimmy: Swimmy<Int> = Swimmy(100) val swimmy2: Swimmy<out Number> = swimmy // OK じゃあ、inはなんなの?
  32. in / out interface SwimmyRunner<T> { fun run(speed: T) }

    -------------------------------------------------------------- val swimmyRunner = object : SwimmyRunner<Any> { override fun run(speed: Any) { ... } } swimmyRunner.run(100) val stringSwimmyRunner: SwimmyRunner<String> = swimmyRunner // NG
  33. in / out interface SwimmyRunner<T> { fun run(speed: T) }

    -------------------------------------------------------------- val swimmyRunner = object : SwimmyRunner<Any> { override fun run(speed: Any) { ... } } swimmyRunner.run(100) val stringSwimmyRunner: SwimmyRunner<String> = swimmyRunner // NG
  34. in / out interface SwimmyRunner<T> { fun run(speed: T) }

    -------------------------------------------------------------- val swimmyRunner = object : SwimmyRunner<Any> { override fun run(speed: Any) { ... } } swimmyRunner.run(100) val stringSwimmyRunner: SwimmyRunner<String> = swimmyRunner // NG
  35. in / out interface SwimmyRunner<in T> { fun run(speed: T)

    } -------------------------------------------------------------- val swimmyRunner = object : SwimmyRunner<Any> { override fun run(speed: Any) { ... } } swimmyRunner.run(100) val stringSwimmyRunner: SwimmyRunner<String> = swimmyRunner // OK
  36. in / out interface SwimmyRunner<in T> { fun run(speed: T)

    } -------------------------------------------------------------- val swimmyRunner = object : SwimmyRunner<Any> { override fun run(speed: Any) { ... } } swimmyRunner.run(100) val stringSwimmyRunner: SwimmyRunner<String> = swimmyRunner // OK