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

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

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
Avatar for Swimmy Swimmy
July 13, 2024
160

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

Avatar for Swimmy

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