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

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
}
このようなコードはカスタムプロパティやコンピューテッドプロパティと呼ばれている。

KPropertyって何?
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

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
}

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

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にクローズされました