0
Help us understand the problem. What are the problem?

posted at

updated at

Organization

【Kotlin】続・引数がデフォルト引数か否かを判定する

前書き

Kotlin 1.7まででは、引数がデフォルト引数か否かを判定する方法が公式には提供されていません。
引数がオブジェクト型のみという制約を付けられるなら、以下の記事の方法で対応可能ですが、この方法は今後利用できなくなる可能性があります

そこで、この記事ではどれだけコードが汚くなってでも引数がデフォルト引数か否かを判定する方法を考えてみます。
この方法はリフレクション等の魔法を用いずに実現可能な範囲で、プリミティブ型にも対応しています。
ただし、かなり非現実的なコードになっています。

サンプルコード

以下のSampleクラスでは、コンストラクタに引数を指定したか否かをfooSpecified/barSpecifiedに格納しています。

class Sample(
    ctxt: DefaultProvider = DefaultProviderImpl(),
    val foo: Int = ctxt.fooDefault,
    val bar: String? = ctxt.barDefault
) {
    sealed interface DefaultProvider {
        val fooDefault: Int
        val barDefault: String?
    }
    private class DefaultProviderImpl : DefaultProvider {
        var fooSpecified: Boolean = true
            private set
        override val fooDefault: Int get() {
            fooSpecified = false
            return -1
        }

        var barSpecified: Boolean = true
            private set
        override val barDefault: String? get() {
            barSpecified = false
            return null
        }
    }

    val fooSpecified: Boolean
    val barSpecified: Boolean

    init {
        val ctxtImpl = ctxt as DefaultProviderImpl

        fooSpecified = ctxtImpl.fooSpecified
        barSpecified = ctxtImpl.barSpecified
    }
}
利用例
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test

@Test
fun 引数指定無し() {
    Sample().apply {
        assertFalse(fooSpecified)
        assertFalse(barSpecified)
    }
}

@Test
fun 引数指定あり() {
    Sample(foo = 1, bar = "2").apply {
        assertTrue(fooSpecified)
        assertTrue(barSpecified)
    }
}

解説

Kotlinでは、デフォルト引数に別のデフォルト引数を用いた値を設定できます。
サンプルコードではこれを利用して、DefaultProviderImplにデフォルト引数の利用状況を記録しています。

考察

まず第一の残念ポイントは、コードが非常に複雑になってしまうことです。
手動で一々こんなものを書いていられません。

第二の残念ポイントは、DefaultProviderが必ず第一引数に来てしまうため、利用側が実質的に名前付き引数の利用を強制されてしまう点です。
Kotlin 1.7現在、デフォルト引数同士の参照は順番の制約が有るため、どうしてもこうなってしまいます。
これが無ければ、使う側が何か意識する必要が無くなったんですが……。

ということで、コード生成するならワンチャン無い位の仕上がりとなりました。
強大な黒魔術に手を染めれば、使い手的にスマートな見栄えは実現できなくもない気がしますが、流石にそこまでやる気にならなかったのでここまでとします。

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
0
Help us understand the problem. What are the problem?