KotlinのSecondary Constructorsでvalのプロパティを初期化したい
やりたかったこと
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
の辺りまで読んだつもりだけど、どっか明確に記載されてたかなぁ…