Closed5

Kotlinのgetterとsetterを見る/移譲 Delegate

mtkw0127mtkw0127

https://kotlinlang.org/docs/properties.html#backing-properties

Kotlinの普段のクラス内で宣言されているプロパティはDeclaring propertiesという。

class Person {
  val name: String = "yamada"
}

フィールドのシンタックスはこうらしい。

var <propertyName>[: <PropertyType>] [= <property_initializer>]
    [<getter>]
    [<setter>]

getter, setter, property_initializerはオプショナル。
PropertyTypeを省略できるのはproperty_initializer・getterの戻り値の型から推測できる場合。

    var name: String = "Sample"
        get() = field
        set(value) {
            field = value
        }

getterとsetterを省略した場合は上記のような感じになっている。
フィールドの値を取得したり設定したいする場合はこのget()とset(value)が実行されている。
fieldはバッキングフィールドと呼ばれている。

setをprivateにしてクラス内からのみsetできるようにすることもできる。

class Sample {
    var name: String = "Sample"
        private set(value) {
            field = value
        }
}

これはメソッド内部を省略して以下のようにも書ける。

class Sample {
   var name: String = "Sample"
       private set
}

JavaだとgetNameやsetNameを作るのが一般的だと思う。そのようなコードを書くのが不要になっている。JavaでgetNameやsetNameをするのはカプセル化のようなフィールドへのアクセス制御をしやすくするため。

class Rectangle(val width: Int, val height: Int) {
    val area: Int // property type is optional since it can be inferred from the getter's return type
        get() = this.width * this.height
}

このようなコードはカスタムプロパティやコンピューテッドプロパティと呼ばれている。

mtkw0127mtkw0127

KPropertyって何?
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.reflect/-k-property/

valやvarで宣言され名前が付けられたプロパティを表すもの。
reflectで定義されている。

    class Person(
      val age: Int,
      val name: String,
    )
    // クラスのKProperty
    val personAgeKProperty: KProperty1<Person, Int> = Person::age
    val personNameKProperty: KProperty1<Person, String> = Person::name

    // インスタンスのKProperty
    val alice = Person(20, "Alice")
    val aliceNameKProperty: KProperty0<String> = alice::name
    val aliceAgeKProperty: KProperty0<Int> = alice::age
mtkw0127mtkw0127

Class Delegation

クラスを継承せずに別クラスの振る舞いを再利用することができる。

interface Language {
    fun greet()
}

class English : Language {
    override fun greet() {
        println("Hello")
    }
}

class Person(
    val age: Int,
    val name: String,
    language: Language,
) : Language by language

fun main() {
    val person = Person(20, "Alice", English())
    person.greet() // Hello
}

mtkw0127mtkw0127
open class Singer() {
    fun sing() {
        println("lalalala...")
    }
}

open class Dancer() {
    fun dance() {
        println("hiphop dance")
    }
}

class SingerA() : Singer()
class SingerB() : Singer()

class DancerA() : Dancer()

class DancerAndSinger() : Singer(), Dancer() // 継承で処理を共通化しようと思っても多重継承ができないのでNG

継承で振る舞いを共通化させようとした場合、多重継承ができないのでつらみが出てくる。

mtkw0127mtkw0127
interface SingSong {
    fun sing()
}

class SingLalala : SingSong {
    override fun sing() {
        println("lalalala...")
    }
}

interface Dance {
    fun dance()
}

class DanceHiphop : Dance {
    override fun dance() {
        println("hiphop dance")
    }
}

class DancerAndSinger : Dance by DanceHiphop(), SingSong by SingLalala()

移譲であればいくらでも振る舞いの再利用ができる。

このスクラップは2024/08/29にクローズされました