byキーワード
インターフェースの実装を委譲します
「byキーワード」はCoddyKit上の無料Kotlin Academyレッスンです。 これはレッスン1/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはKotlin Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Kotlin Academyコースには全4レッスンが含まれています。
委譲とは
クラス委譲を使うと、あるオブジェクトがインターフェースの実装を別のオブジェクトに任せられます。Kotlinでは、byキーワードによってこの機能が言語に組み込まれています。
委譲は、コンポジションを手軽に実現する仕組みです。
委譲するインターフェース
まず、インターフェースと具体的な実装を用意します。後でこの実装に委譲します。
interface Speaker {
fun speak(): String
}
class LoudSpeaker : Speaker {
override fun speak() = "HELLO!"
}
fun main() {
println(LoudSpeaker().speak())
}byキーワード
class X(d: Iface) : Iface by dと記述します。コンパイラーは、インターフェースの各メンバーについて、dを呼び出す転送メソッドを生成します。
interface Speaker {
fun speak(): String
}
class LoudSpeaker : Speaker {
override fun speak() = "HELLO!"
}
class Announcer(s: Speaker) : Speaker by s
fun main() {
val a = Announcer(LoudSpeaker())
println(a.speak())
}ボイラープレート不要
byを使わない場合は、インターフェースの各関数について転送メソッドを手作業で記述する必要があります。委譲を使えば、それらがすべて自動的に生成されます。
interface Repository {
fun save(item: String): String
fun count(): Int
}
class MemoryRepo : Repository {
private val items = mutableListOf<String>()
override fun save(item: String): String { items.add(item); return item }
override fun count() = items.size
}
class LoggingRepo(r: Repository) : Repository by r
fun main() {
val repo = LoggingRepo(MemoryRepo())
repo.save("a")
println(repo.count())
}一部のメンバーをオーバーライドする
カスタマイズしたいメンバーだけをオーバーライドできます。それ以外のメンバーは引き続き委譲先へ転送されます。
interface Repository {
fun save(item: String): String
fun count(): Int
}
class MemoryRepo : Repository {
private val items = mutableListOf<String>()
override fun save(item: String): String { items.add(item); return item }
override fun count() = items.size
}
class LoggingRepo(private val r: Repository) : Repository by r {
override fun save(item: String): String {
println("saving " + item)
return r.save(item)
}
}
fun main() {
val repo = LoggingRepo(MemoryRepo())
repo.save("book")
println(repo.count())
}パラメーターとして委譲先を渡す
委譲先は通常、プライマリコンストラクター経由で渡します。一部のメンバーもオーバーライドする場合は、private valで参照を保持できます。
interface Clock { fun now(): Int }
class FixedClock : Clock { override fun now() = 12 }
class App(private val clock: Clock) : Clock by clock
fun main() {
println(App(FixedClock()).now())
}標準インターフェースへの委譲
組み込みのコレクションインターフェースにも委譲できるため、基になるコレクションと同じように振る舞うラッパーをすぐに作成できます。
class TaggedList(private val inner: List<String>) : List<String> by inner {
val tag = "v1"
}
fun main() {
val tl = TaggedList(listOf("x", "y"))
println(tl.size)
println(tl[0])
println(tl.tag)
}コンパイルの仕組み
内部では、コンパイラーが委譲先を保持し、転送実装を生成します。生成されたメソッドは、対応する委譲先のメソッドを呼び出すだけです。手書きの転送と同じですが、記述の手間がありません。
キャプチャされるスナップショット
重要な注意点があります。委譲先への参照は、オブジェクトの構築時にキャプチャされます。オーバーライドしたメンバーは委譲先を明示的に呼び出しますが、生成された転送メソッドは最初に渡されたインスタンスを使います。
interface Source { fun value(): Int }
class Const(val n: Int) : Source { override fun value() = n }
class Wrapper(s: Source) : Source by s
fun main() {
val w = Wrapper(Const(42))
println(w.value())
}byを使う理由
byキーワードを使うと、次のことができます。
- 既存の実装を再利用する
- サブクラス化せずに振る舞いを追加する
- 壊れやすい継承階層を避ける
これは、継承よりもコンポジションを優先する原則をKotlinで本格的にサポートする機能です。
基盤
このbyキーワードは、このコースのすべての基盤となります。次は、メンバーオブジェクトへの委譲によって振る舞いを組み立てる方法を詳しく見ていきます。
理解度チェック
byキーワードについての理解度を確認しましょう。
まとめ
byを使ったクラス委譲について、次のことを学びました。
- インターフェースを別のオブジェクトに委譲する
- 転送メソッドが自動生成される
- 一部のメンバーをオーバーライドし、残りを転送する
- 組み込みのインターフェースにも使える
次は、メンバーへの委譲によって振る舞いを組み立てます。
よくある質問
「byキーワード」レッスンは無料ですか?
はい。「byキーワード」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Kotlin Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Kotlin Academyコースには全4レッスンが含まれています。
「byキーワード」で何を学びますか?
インターフェースの実装を委譲します ブラウザで直接実行するハンズオンコードでKotlin Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Kotlin Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのKotlin Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。
「byキーワード」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このKotlin Academyレッスンでコードを書いて実行できますか?
はい。すべてのKotlin Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。