- Kotlinスタートブック読書勉強会 #7 で LTした内容
- 第10章 の 写経
reference や他のサイトなどに記述があって、赤べこ本では割愛されている(または ver1.1 以降の)内容の引用 が中心
// list 10.9 を 改修
interface Greeter {
fun sayHello(target: String = "匿名")
}
class JapaneseGreeter : Greeter {
override fun sayHello(target: String) {
println("こんにちは, ${target}さん!")
}
}
fun main(args: Array<String>) {
val greeter = JapaneseGreeter()
greeter.sayHello() // こんにちは, 匿名さん!
greeter.sayHello("たろう") // こんにちは, たろうさん!
}
- 第9章 の 補足 に記述したとおり, override 先で デフォルト引数 を指定するとエラー
- 以下の ソース例 参照
- Classes and Objects - Classes and Inheritance - Inheritance - Overriding Rules
- Classes and Objects - Interfaces - Resolving overriding conflicts
- 複数 の class delegation が可能
- -> 「 KotlinでDelegation #kansaikt 」(p23) 参照
interface AListener {
fun a1(); fun ab()
}
class AAdapter : AListener {
override fun a1() = println("AAdapter:a1")
override fun ab() = println("AAdapter:ab")
}
interface BListener {
fun b1(); fun ab()
}
class BAdapter : BListener {
override fun b1() = println("BAdapter:b1")
override fun ab() = println("BAdapter:ab")
}
// ★ 複数 の class delegation
class MyListener(private val a: AListener, private val b: BListener) : AListener by a, BListener by b {
override fun ab() { // `ab` の定義が衝突しているので override しないと コンパイルエラー
b.ab()
}
}
fun main(args: Array<String>) {
val l = MyListener(AAdapter(), BAdapter())
l.a1() // AAdapter:a1
l.b1() // BAdapter:b1
l.ab() // BAdapter:ab
}
class InstrumentedHashSet<E> : HashSet<E>() {
var addCount: Int = 0
override fun add(e: E): Boolean {
addCount++
return super.add(e)
}
override fun addAll(c: Collection<E>): Boolean {
addCount += c.size
return super.addAll(c)
}
}
fun main(args: Array<String>) {
val s1 = InstrumentedHashSet<String>()
s1.add("a")
s1.add("b");
println(s1.addCount) // 2
val s2 = InstrumentedHashSet<String>()
s2.addAll(arrayOf("x", "y", "z"))
println(s2.addCount) // 6
}
3
を期待していたのに 結果は6
super.addAll
の中でadd
が呼ばれているため
委譲に変更した例
// 変更したのは先頭行のみ. 他の実装は 継承の場合 と一緒
class InstrumentedHashSet<E>(private val s: MutableSet<E>) : MutableSet<E> by s {
var addCount: Int = 0
override fun add(e: E): Boolean {
addCount++
return s.add(e)
}
override fun addAll(c: Collection<E>): Boolean {
addCount += c.size
return s.addAll(c)
}
}
fun main(args: Array<String>) {
val s1 = InstrumentedHashSet(HashSet<String>()) // コンストラクタ で 委譲先 の インスタンス を渡す
s1.add("a")
s1.add("b");
println(s1.addCount) // 2
val s2 = InstrumentedHashSet(HashSet<String>()) // 同上
s2.addAll(arrayOf("x", "y", "z"))
println(s2.addCount) // 3
}
- Delegation とは話がずれますが, 継承の落とし穴関連で Effective java からもう一例。
- 【Effective Java】項目17:継承のための設計および文書化する、でなければ継承を禁止する - The King's Museum 参照
- Effective java2nd chap04 (p20) 参照
- 継承を可能にするための制約
- コンストラクタ は override 可能なメソッドを呼び出してはいけない
- 継承を可能にするための制約
import java.time.LocalDateTime
fun Super.s() = println("... init start (${javaClass.kotlin.simpleName})")
fun Super.e() = println("... init end (${javaClass.kotlin.simpleName})")
fun p() = println("-----------------------------")
abstract class Super {
init {
s()
overrideMe() // ★[NG] コンストラクタ が override 可能なメソッドを呼び出し
e()
}
abstract fun overrideMe()
}
class Sub : Super() {
private var date = LocalDateTime.now()
// ★ super クラス の init 呼び出し時点では `date` は まだ `null`
override fun overrideMe() = println(date)
}
class Sub2 : Super() {
private var date = LocalDateTime.now()
init { // 親の init が 呼ばれた後, 実行される
s()
overrideMe() // sub クラス の init 呼び出し時点では `date` は 初期化済み
e()
}
override fun overrideMe() = println(date)
}
fun main(args: Array<String>) {
Sub()
p()
Sub2()
}
- 実行結果
... init start (Sub)
null
... init end (Sub)
-----------------------------
... init start (Sub2)
null
... init end (Sub2)
... init start (Sub2)
2017-11-27T18:32:08.281
... init end (Sub2)