Kotlinの委任プロパティ

Kotlinの委任プロパティ

1. 前書き

Kotlinプログラミング言語は、クラスプロパティをネイティブでサポートしています。

通常、プロパティは対応するフィールドによって直接サポートされますが、常にこのようにする必要はありません。外部に正しく公開されている限り、プロパティと見なすことができます。

これは、ゲッターとセッターでこれを処理するか、Delegates.のパワーを活用することで実現できます。

2. 委任プロパティとは何ですか?

Simply put, delegated properties are not backed by a class field and delegate getting and setting to another piece of code.これにより、委任された機能を抽象化し、複数の同様のプロパティ間で共有できます。 個別のフィールドではなく、マップにプロパティ値を保存します。

委任プロパティは、プロパティとそれが使用するデリゲートを宣言することで使用されます。 The by keyword indicates that the property is controlled by the provided delegate instead of its own field.

例えば:

class DelegateExample(map: MutableMap) {
    var name: String by map
}

これは、MutableMap自体がデリゲートであるという事実を利用しており、そのキーをプロパティとして扱うことができます。

3. 標準の委任プロパティ

Kotlin標準ライブラリには、すぐに使用できる標準デリゲートのセットが付属しています。

MutableMapを使用して可変プロパティをバックアップする例はすでに見てきました。 同様に、Mapを使用して不変のプロパティをバックアップできます。これにより、個々のフィールドにプロパティとしてアクセスできますが、変更することはできません。

The lazy delegate allows the value of a property to be computed only on first access and then cached.これは、計算にコストがかかる可能性があり、データベースからロードするなど、必要がない可能性があるプロパティに役立ちます。

class DatabaseBackedUser(userId: String) {
    val name: String by lazy {
        queryForValue("SELECT name FROM users WHERE userId = :userId", mapOf("userId" to userId)
    }
}

The observable delegate allows for a lambda to be triggered any time the value of the property changes、たとえば、変更通知または他の関連プロパティの更新を許可します。

class ObservedProperty {
    var name: String by Delegates.observable("") {
        prop, old, new -> println("Old value: $old, New value: $new")
    }
}

4. 代理人の作成

既に存在するデリゲートを使用するのではなく、デリゲートを作成したい場合があります。 This relies on writing a class that extends one of two interfaces – ReadOnlyProperty or ReadWriteProperty.

これらのインターフェースは両方とも、getValueと呼ばれるメソッドを定義します。これは、委任されたプロパティが読み取られたときにその現在の値を提供するために使用されます。 これは2つの引数を取り、プロパティの値を返します。

  • thisRef –プロパティが含まれるクラスへの参照

  • property –委任されているプロパティの反映の説明

ReadWritePropertyインターフェースは、プロパティが書き込まれるときにプロパティの現在の値を更新するために使用されるsetValueと呼ばれるメソッドを追加で定義します。 これは3つの引数を取り、戻り値はありません。

  • thisRef –プロパティが含まれるクラスへの参照

  • property –委任されているプロパティの反映の説明

  • value –プロパティの新しい値

例として、ローカルフィールドではなくデータベース接続に関して常に機能するデリゲートを作成しましょう。

class DatabaseDelegate(readQuery: String, writeQuery: String, id: Any) : ReadWriteDelegate {
    fun getValue(thisRef: R, property: KProperty<*>): T {
        return queryForValue(readQuery, mapOf("id" to id))
    }

    fun setValue(thisRef: R, property: KProperty<*>, value: T) {
        update(writeQuery, mapOf("id" to id, "value" to value))
    }
}

これは、データベースにアクセスするための2つのトップレベル関数に依存しています。

  • queryForValue –これはいくつかのSQLといくつかのバインドを取り、最初の値を返します

  • update –これはいくつかのSQLといくつかのバインドを取り、それをUPDATEステートメントとして扱います

その後、これを通常のデリゲートと同様に使用し、クラスをデータベースに自動的にバックアップさせることができます。

class DatabaseUser(userId: String) {
    var name: String by DatabaseDelegate(
      "SELECT name FROM users WHERE userId = :id",
      "UPDATE users SET name = :value WHERE userId = :id",
      userId)
    var email: String by DatabaseDelegate(
      "SELECT email FROM users WHERE userId = :id",
      "UPDATE users SET email = :value WHERE userId = :id",
      userId)
}

5. 概要

プロパティの委任は強力な手法であり、他のプロパティの制御を引き継ぐコードを記述でき、このロジックを異なるクラス間で簡単に共有できます。 これにより、通常のプロパティアクセスのような外観と操作性を備えた、堅牢で再利用可能なロジックが可能になります。

この記事の完全に機能する例は、over on GitHubにあります。