KotlinのClass Delegationおさらい #Kotlin_Sansan

KotlinのClass Delegationおさらい #Kotlin_Sansan

14c9795d267f5b85abb98ca5e8780646?s=128

Taro Nagasawa

July 01, 2016
Tweet

Transcript

  1. Kotlinの Class Delegation おさらい 2016-06-29 第3回Kotlin勉強会@Sansan 長澤 太郎 @ngsw_taro

  2. 差分プログ ラミング

  3. 差分プログ ラミング

  4. Effective Java 項目16 「継承よりコンポジションを選ぶ」

  5. Class Delegation が便利です

  6. 自己紹介 • 長澤 太郎 たろーって呼んでね • @ngsw_taro • プログラマー@エムスリー株式会社 ◦

    Android, Kotlin, Java, Scala, Rubyなど • Kotlinエバンジェリスト(JetBrains黙認) ◦ 日本Kotlinユーザグループ代表 • やすべえとディズニーが好き
  7. 例題 要素が追加された 回数を記憶する Setを作成する。

  8. 差分プログラミング

  9. class CountingHashSet<E> : java.util.HashSet<E>() { var addCount: Int = 0

    private set override fun add(element: E): Boolean { addCount++ return super.add(element) } override fun addAll(elements: Collection<E>): Boolean { addCount += elements.size return super.addAll(elements) } }
  10. class CountingHashSet<E> : java.util.HashSet<E>() { var addCount: Int = 0

    private set override fun add(element: E): Boolean { addCount++ return super.add(element) } override fun addAll(elements: Collection<E>): Boolean { addCount += elements.size return super.addAll(elements) } } クラスの継承
  11. class CountingHashSet<E> : java.util.HashSet<E>() { var addCount: Int = 0

    private set override fun add(element: E): Boolean { addCount++ return super.add(element) } override fun addAll(elements: Collection<E>): Boolean { addCount += elements.size return super.addAll(elements) } } 回数保持のプロパティ
  12. class CountingHashSet<E> : java.util.HashSet<E>() { var addCount: Int = 0

    private set override fun add(element: E): Boolean { addCount++ return super.add(element) } override fun addAll(elements: Collection<E>): Boolean { addCount += elements.size return super.addAll(elements) } } 具象メソッドのオーバライド
  13. class CountingHashSet<E> : java.util.HashSet<E>() { var addCount: Int = 0

    private set override fun add(element: E): Boolean { addCount++ return super.add(element) } override fun addAll(elements: Collection<E>): Boolean { addCount += elements.size return super.addAll(elements) } } カウント +1
  14. class CountingHashSet<E> : java.util.HashSet<E>() { var addCount: Int = 0

    private set override fun add(element: E): Boolean { addCount++ return super.add(element) } override fun addAll(elements: Collection<E>): Boolean { addCount += elements.size return super.addAll(elements) } } 具象メソッドのオーバライド
  15. class CountingHashSet<E> : java.util.HashSet<E>() { var addCount: Int = 0

    private set override fun add(element: E): Boolean { addCount++ return super.add(element) } override fun addAll(elements: Collection<E>): Boolean { addCount += elements.size return super.addAll(elements) } } カウント +要素数
  16. class CountingHashSet<E> : java.util.HashSet<E>() { var addCount: Int = 0

    private set override fun add(element: E): Boolean { addCount++ return super.add(element) } override fun addAll(elements: Collection<E>): Boolean { addCount += elements.size return super.addAll(elements) } } よさそう!わずか15行!
  17. 試してみる val set = CountingHashSet<Int>() set.add(1) set.add(2) println(set.addCount) set.addAll(setOf(3, 4,

    5)) println(set.addCount)
  18. 試してみる val set = CountingHashSet<Int>() set.add(1) set.add(2) println(set.addCount) // 2

    set.addAll(setOf(3, 4, 5)) println(set.addCount) 2回 addを呼び出している
  19. 試してみる val set = CountingHashSet<Int>() set.add(1) set.add(2) println(set.addCount) set.addAll(setOf(3, 4,

    5)) println(set.addCount) // 5? 3つ追加している
  20. 試してみる val set = CountingHashSet<Int>() set.add(1) set.add(2) println(set.addCount) set.addAll(setOf(3, 4,

    5)) println(set.addCount) // まさかの8
  21. class CountingHashSet<E> : java.util.HashSet<E>() { var addCount: Int = 0

    private set override fun add(element: E): Boolean { addCount++ return super.add(element) } override fun addAll(elements: Collection<E>): Boolean { addCount += elements.size return super.addAll(elements) } } HashSet#addAllは addを呼び出す実装になっている なぜ期待通りに動かなかったのか
  22. class CountingHashSet<E> : java.util.HashSet<E>() { var addCount: Int = 0

    private set override fun add(element: E): Boolean { addCount++ return super.add(element) } override fun addAll(elements: Collection<E>): Boolean { addCount += elements.size return super.addAll(elements) } } ここでカウントして なぜ期待通りに動かなかったのか
  23. class CountingHashSet<E> : java.util.HashSet<E>() { var addCount: Int = 0

    private set override fun add(element: E): Boolean { addCount++ return super.add(element) } override fun addAll(elements: Collection<E>): Boolean { addCount += elements.size return super.addAll(elements) } } HashSetの実装を呼び なぜ期待通りに動かなかったのか
  24. class CountingHashSet<E> : java.util.HashSet<E>() { var addCount: Int = 0

    private set override fun add(element: E): Boolean { addCount++ return super.add(element) } override fun addAll(elements: Collection<E>): Boolean { addCount += elements.size return super.addAll(elements) } } addが呼び出されると、二重でカウント なぜ期待通りに動かなかったのか
  25. どうすればいいか

  26. どうすればいいか addAllでカウントしない

  27. どうすればいいか addAllでカウントしない まぁ確かに...でも

  28. どうすればいいか addAllでカウントしない まぁ確かに...でも スーパクラスの実装に依存、脆い

  29. コンポジション

  30. class CountingSet<E>(private val set: MutableSet<E>) : MutableSet<E> { var addCount:

    Int = 0 private set override fun add(element: E): Boolean { addCount++ return set.add(element) } override fun addAll(elements: Collection<E>): Boolean { addCount += elements.size return set.addAll(elements) } ...
  31. class CountingSet<E>(private val set: MutableSet<E>) : MutableSet<E> { var addCount:

    Int = 0 private set override fun add(element: E): Boolean { addCount++ return set.add(element) } override fun addAll(elements: Collection<E>): Boolean { addCount += elements.size return set.addAll(elements) } ... インタフェースを実装
  32. class CountingSet<E>(private val set: MutableSet<E>) : MutableSet<E> { var addCount:

    Int = 0 private set override fun add(element: E): Boolean { addCount++ return set.add(element) } override fun addAll(elements: Collection<E>): Boolean { addCount += elements.size return set.addAll(elements) } ... このへんがコンポジション: 実装を継承するのではなく、 実装を保持する。
  33. class CountingSet<E>(private val set: MutableSet<E>) : MutableSet<E> { var addCount:

    Int = 0 private set override fun add(element: E): Boolean { addCount++ return set.add(element) } override fun addAll(elements: Collection<E>): Boolean { addCount += elements.size return set.addAll(elements) } ... さっきと同じ
  34. class CountingSet<E>(private val set: MutableSet<E>) : MutableSet<E> { var addCount:

    Int = 0 private set override fun add(element: E): Boolean { addCount++ return set.add(element) } override fun addAll(elements: Collection<E>): Boolean { addCount += elements.size return set.addAll(elements) } ... その他のコードは省略
  35. 試してみる val set = CountingSet(HashSet<Int>()) set.add(1) set.add(2) println(set.addCount) // 2

    set.addAll(setOf(3, 4, 5)) println(set.addCount) // 期待通りの5 実装をコンストラクタで渡す
  36. class CountingSet<E>(private val set: MutableSet<E>) : MutableSet<E> { var addCount:

    Int = 0 private set override fun add(element: E): Boolean { addCount++ return set.add(element) } override fun addAll(elements: Collection<E>): Boolean { addCount += elements.size return set.addAll(elements) } ... その他のコードは省略
  37. class CountingSet<E>(private val set: MutableSet<E>) : MutableSet<E> { var addCount:

    Int = 0 private set override fun add(element: E): Boolean { addCount++ return set.add(element) } override fun addAll(elements: Collection<E>): Boolean { addCount += elements.size return set.addAll(elements) } ... 他にオーバライドすべきメソッドたち
  38. override val size: Int get() = set.size override fun contains(element:

    E): Boolean = set.contains(element) override fun remove(element: E): Boolean = set.remove(element) override fun containsAll(elements: Collection<E>): Boolean = set.containsAll(elements) override fun isEmpty(): Boolean = set.isEmpty() override fun clear() { set.clear() } override fun iterator(): MutableIterator<E> = set.iterator() override fun removeAll(elements: Collection<E>): Boolean = set.removeAll(elements) override fun retainAll(elements: Collection<E>): Boolean = set.retainAll(elements) 他にオーバライドすべきメソッドたち
  39. override val size: Int get() = set.size override fun contains(element:

    E): Boolean = set.contains(element) override fun remove(element: E): Boolean = set.remove(element) override fun containsAll(elements: Collection<E>): Boolean = set.containsAll(elements) override fun isEmpty(): Boolean = set.isEmpty() override fun clear() { set.clear() } override fun iterator(): MutableIterator<E> = set.iterator() override fun removeAll(elements: Collection<E>): Boolean = set.removeAll(elements) override fun retainAll(elements: Collection<E>): Boolean = set.retainAll(elements) 他にオーバライドすべきメソッドたち めんど くさい!
  40. 4. Class Delegation

  41. class CountingSet<E>(private val set: MutableSet<E>) : MutableSet<E> by set {

    var addCount: Int = 0 private set override fun add(element: E): Boolean { addCount++ return set.add(element) } override fun addAll(elements: Collection<E>): Boolean { addCount += elements.size return set.addAll(elements) } }
  42. class CountingSet<E>(private val set: MutableSet<E>) : MutableSet<E> by set {

    var addCount: Int = 0 private set override fun add(element: E): Boolean { addCount++ return set.add(element) } override fun addAll(elements: Collection<E>): Boolean { addCount += elements.size return set.addAll(elements) } } このおまじないが Class Delegation
  43. class CountingSet<E>(private val set: MutableSet<E>) : MutableSet<E> by set {

    var addCount: Int = 0 private set override fun add(element: E): Boolean { addCount++ return set.add(element) } override fun addAll(elements: Collection<E>): Boolean { addCount += elements.size return set.addAll(elements) } } ここは依然変わらず
  44. class CountingSet<E>(private val set: MutableSet<E>) : MutableSet<E> by set {

    var addCount: Int = 0 private set override fun add(element: E): Boolean { addCount++ return set.add(element) } override fun addAll(elements: Collection<E>): Boolean { addCount += elements.size return set.addAll(elements) } } 他にオーバライドすべきメソッドたち を明示的に記述する必要がない!
  45. 試してみる(コードはさっきと同じ) val set = CountingSet(HashSet<Int>()) set.add(1) set.add(2) println(set.addCount) // 2

    set.addAll(setOf(3, 4, 5)) println(set.addCount) // 期待通りの5
  46. まとめ • 実装の継承には十分注意する • インタフェースに依存し、実装は外部から注入 • Class Delegationを使うと、簡単に記述できる

  47. 突然の宣伝 • Kotlinスタートブック (リックテレコム) • 目印は赤べこ • 7/17頃 発売 •

    360ページ • 今回の話のように 文法だけでなく、その 背景や周辺の事情に ついても言及しています
  48. Thank you Enjoy Kotlin