カスタムReadWritePropertyデリゲートの作成
ReadWritePropertyを使って、カスタムプロパティデリゲートをゼロから実装します。
「カスタムReadWritePropertyデリゲートの作成」はCoddyKit上の無料Kotlin Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはKotlin Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Kotlin Academyコースには全4レッスンが含まれています。
ReadWritePropertyインターフェース
カスタムデリゲートは、getValueおよびsetValue演算子を備えたReadWriteProperty<ThisRef, T>を実装します。
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class SimpleDelegate<T>(private var value: T) : ReadWriteProperty<Any?, T> {
override fun getValue(thisRef: Any?, property: KProperty<*>): T = value
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
this.value = value
}
}
var x: Int by SimpleDelegate(0)
fun main() { x = 42; println(x) }val用のReadOnlyProperty
読み取り専用のvalプロパティでは、getValueだけを備えたReadOnlyProperty<ThisRef, T>を実装します。
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
class Constant<T>(private val value: T) : ReadOnlyProperty<Any?, T> {
override fun getValue(thisRef: Any?, property: KProperty<*>): T = value
}
val pi: Double by Constant(3.14159)
fun main() { println(pi) }コンテキストにthisRefを使う
thisRefを使うと、デリゲートから外側のオブジェクトにアクセスでき、コンテキストに応じた動作を実現できます。
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class LoggingDelegate<T>(private var v: T) : ReadWriteProperty<Any?, T> {
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
println("[${thisRef?.javaClass?.simpleName}] get ${property.name} = $v")
return v
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
println("[${thisRef?.javaClass?.simpleName}] set ${property.name} = $value")
v = value
}
}
class Config { var host: String by LoggingDelegate("localhost") }
fun main() { val c = Config(); c.host = "prod.example.com"; println(c.host) }数値を範囲内に収めるデリゲート
数値を範囲内に収め、不変条件を透過的に適用するデリゲートです。
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class Clamped(private val min: Int, private val max: Int, initial: Int) : ReadWriteProperty<Any?, Int> {
private var v = initial.coerceIn(min, max)
override fun getValue(t: Any?, p: KProperty<*>) = v
override fun setValue(t: Any?, p: KProperty<*>, value: Int) {
v = value.coerceIn(min, max)
}
}
var brightness: Int by Clamped(0, 255, 128)
fun main() {
brightness = 300; println(brightness) // 255
brightness = -5; println(brightness) // 0
brightness = 100; println(brightness) // 100
}null許容キャッシュデリゲート
値を一度計算してキャッシュし、nullを設定することで無効化できるデリゲートです。
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class CachedNullable<T>(private val compute: () -> T) : ReadWriteProperty<Any?, T?> {
private var cache: T? = null
override fun getValue(t: Any?, p: KProperty<*>): T? = cache ?: compute().also { cache = it }
override fun setValue(t: Any?, p: KProperty<*>, value: T?) { cache = value }
}
var expensiveResult: Int? by CachedNullable { (1..1000).sum() }
fun main() {
println(expensiveResult) // computed
println(expensiveResult) // cached
expensiveResult = null
println(expensiveResult) // recomputed
}@Synchronizedを使ったスレッドセーフなデリゲート
@Synchronizedまたは内部のロックオブジェクトを使って、デリゲートにスレッドセーフ性を追加します。
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class SynchronizedDelegate<T>(private var v: T) : ReadWriteProperty<Any?, T> {
@Synchronized
override fun getValue(t: Any?, p: KProperty<*>) = v
@Synchronized
override fun setValue(t: Any?, p: KProperty<*>, value: T) { v = value }
}
var counter: Int by SynchronizedDelegate(0)
fun main() { counter = 99; println(counter) }デリゲートファクトリ関数パターン
標準ライブラリのスタイルに従い、デリゲートオブジェクトを返す拡張関数を通じてデリゲートを公開します。
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
fun <T> logged(initial: T): ReadWriteProperty<Any?, T> = object : ReadWriteProperty<Any?, T> {
var v = initial
override fun getValue(t: Any?, p: KProperty<*>): T { println("get ${p.name}"); return v }
override fun setValue(t: Any?, p: KProperty<*>, value: T) { println("set ${p.name}=$value"); v = value }
}
var name: String by logged("world")
fun main() { println(name); name = "Kotlin" }KPropertyメタデータを使うデリゲート
KProperty引数からプロパティ名とアノテーションを取得できます。ログ出力フレームワークやORMで便利です。
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class AuditDelegate<T>(private var v: T) : ReadWriteProperty<Any?, T> {
private val history = mutableListOf<String>()
override fun getValue(t: Any?, p: KProperty<*>) = v
override fun setValue(t: Any?, p: KProperty<*>, value: T) {
history.add("${p.name}: $v -> $value")
v = value
}
fun printHistory() = history.forEach(::println)
}
var status: String by AuditDelegate("idle").also { /* expose audit */ }provideDelegate演算子
provideDelegateを使うと、デリゲートが委譲の設定に介入し、初期化時にプロパティ名やアノテーションを検証できます。
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
class ValidatedDelegate<T>(private val v: T) {
operator fun provideDelegate(t: Any?, p: KProperty<*>): ReadOnlyProperty<Any?, T> {
require(p.name.isNotBlank()) { "Property name must not be blank" }
return ReadOnlyProperty { _, _ -> v }
}
}
val greeting: String by ValidatedDelegate("Hello")
fun main() { println(greeting) }アプローチの比較
カスタムデリゲートとobservable/vetoableの比較です。単純なリアクティブパターンには組み込み機能を使用し、複雑な不変条件、キャッシュ、またはコンテキストに応じた動作にはカスタムデリゲートを記述します。
// Built-in: simple change notification
import kotlin.properties.Delegates
var simple: Int by Delegates.observable(0) { _,_,v -> println("changed: $v") }
// Custom: clamping + logging
var complex: Int by run {
object : kotlin.properties.ReadWriteProperty<Any?,Int> {
var v = 0
override fun getValue(t:Any?,p:kotlin.reflect.KProperty<*>)=v
override fun setValue(t:Any?,p:kotlin.reflect.KProperty<*>,value:Int){
println("clamping $value"); v=value.coerceIn(0,100)
}
}
}
fun main() { simple=5; complex=200; println(complex) }実践:SharedPreferences デリゲート
Androidでよく使われるパターンです。プロパティをSharedPreferencesに委譲すると、読み書きが透過的にディスクへ永続化されます。
// Pseudocode (no Android runtime here)
class PrefDelegate(private val key: String, private val default: String) {
// In real Android code:
// override fun getValue(...) = prefs.getString(key, default) ?: default
// override fun setValue(...) { prefs.edit().putString(key, value).apply() }
fun getValue(): String = default // simplified
}
fun main() {
val delegate = PrefDelegate("user_name", "Guest")
println(delegate.getValue())
}クイックチェック
読み書き可能なデリゲートは、どのインターフェースを実装する必要がありますか。
まとめ
カスタムデリゲートはReadWriteProperty(またはReadOnlyProperty)を実装し、ロギング、キャッシュ、検証、値のクランプ、プラットフォームストレージなどを、透過的なプロパティインターフェースの背後にカプセル化できます。
よくある質問
「カスタムReadWritePropertyデリゲートの作成」レッスンは無料ですか?
はい。「カスタムReadWritePropertyデリゲートの作成」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Kotlin Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Kotlin Academyコースには全4レッスンが含まれています。
「カスタムReadWritePropertyデリゲートの作成」で何を学びますか?
ReadWritePropertyを使って、カスタムプロパティデリゲートをゼロから実装します。 ブラウザで直接実行するハンズオンコードでKotlin Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Kotlin Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのKotlin Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。
「カスタムReadWritePropertyデリゲートの作成」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このKotlin Academyレッスンでコードを書いて実行できますか?
はい。すべてのKotlin Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- by lazy:遅延初期化を深く学ぶ
- observableとvetoableデリゲート
- カスタムReadWritePropertyデリゲートの作成
- Preferences、Map委譲プロパティ、ロギングのデリゲート