KotlinのSecondary Constructorsでvalのプロパティを初期化したい
2019-06-06 / Kotlin
やりたかったこと
first
に依存しているsecond
を保持するPair
のようなモノが欲しかった。
data class Pair<F, S>(
val first: F? = null,
val second: S? = null
)
fun <F, S> newPair(first: F?, block: (F) -> S?) = Pair(
first = first,
second = first?.let(block) // firstがnullでなければblockを呼び出す
)
newPair
は、以下のような使い方を想定している。
val pair = newPair(getFirst()) {first ->
getSecond(first)
}
first
、second
ともにNullableだがblock
の実行はfirst
がnullではない状態を保証したい。
別にこのままでも良いんだけど、ちょっとコンストラクタにしてみようと思いたった。このままでも良かったのにKotlin力が高くないので、アレコレ試行錯誤が必要だった。あとcompanion object
という方法もあったけど、まぁやってみたかったんだよ。
thisへの委譲では式が使える
Secondary Constructorsでは、Primary Constructorか他のSecondary Constructorへの委譲が必要になる。this
を使用して呼び出しを行うが、その際には式を利用することができる。なので、こういう呼び出し方ができる。
data class Pair<F, S>(
val first: F? = null,
val second: S? = null
) {
// OK
constructor(first: F?, block: (F) -> S?): this(first, first?.let(block))
}
このことがわかってなくてあれこれ試行錯誤した。
ダメな例
プロパティがval
であるということが問題になった。
data class Pair<F, S>(
val first: F? = null,
val second: S? = null
) {
// NG: Val cannot be reassigned
// secondはvalなので書き換えができない
// コンストラクタなのになんで初期化できんのじゃーとかちょっと思ったりする
constructor(first: F?, block: (F) -> S?): this(first) {
second = first?.let(block)
}
// NG: Unresolved reference: sec
// thisの方に変数を渡せるのでは???と思ったけど渡せなかった
constructor(first: F?, block: (F) -> S?): this(first, sec) {
val sec = first?.let(block)
}
// OK
// ifも式なのでこれでも問題ない
constructor(first: F?, block: (F) -> S?): this(first, if (first != null) block(first) else null)
// OK
constructor(first: F?, block: (F) -> S?): this(first, first?.let(block))
}
実行例
以下のように期待する動きをしている。
val p1 = Pair("aaa", "AAA")
val p2 = Pair("aaa", String::toUpperCase)
val p3 = Pair<String, String>(null, String::toUpperCase)
println(p1)
println(p2)
println(p3)
// output
// Pair(first=aaa, second=AAA)
// Pair(first=aaa, second=AAA)
// Pair(first=null, second=null)
継承のところで気づいた
リファレンスの継承の辺りを読んでいる時に、以下の記述を見つけて、これはPrimary Constructorの委譲でも使えるのではと思って試したら動いた。
class Derived(
name: String,
val lastName: String
) : Base(name.capitalize().also { println("Argument for Base: $it") }) {
...
リファレンスはCollection
の辺りまで読んだつもりだけど、どっか明確に記載されてたかなぁ…