web-dev-qa-db-ja.com

Kotlinで明示的にまたは暗黙的に渡された関数引数のデフォルト値を区別する方法はありますか?

次のようなkotlin関数を想定します。

fun f(p1: T1? = null, p2: T2? = null, ..., pN: TN? = null) {
    // ...
}

上記の関数の実装は、最初の呼び出しがp1 = nullを暗黙的に渡し、2番目の呼び出しが明示的に渡した次の2つの呼び出しを区別できますか?

f()          // Implicit
f(null)      // Explicit
f(p1 = null) // Explicit

注:任意の数のパラメーターが存在する可能性があります

5
Lukas Eder

本番環境ではこのアプローチを使用しませんが、次のスニペットで行ったようなことができます。

_object Default {
    val defaultMapping = mutableMapOf<KClass<*>, Any?>()

    inline fun <reified T> get(): T? =
            T::class.let {
                defaultMapping[it] ?: it.Java.constructors.getOrNull(0)?.let { c ->
                    try {
                        // NOTE: for now only parameterles constructor will work
                        c.newInstance()
                    } catch (e: Exception) {
                        e.printStackTrace()
                        null
                    }.also { v ->
                        defaultMapping[it] = v
                    }
                } ?: run {
                    defaultMapping[it] = null
                    null
                }
            } as? T

    inline fun <reified T> T.isDefault(): Boolean = defaultMapping[T::class] == this
}

inline fun <reified T> foo(bar: T? = Default.get()) {
    if (bar?.isDefault() == true) println("bar: default is in use")
    else println("bar: $bar")
}

fun main() {
    foo<Any>()
    foo(Default.get<Any>())
    foo<Any>(null)
    foo<Any>(bar = null)
    foo(Any())
    val a = Any()
    foo(a)
    foo(bar = a)
}
_

注意してください、私はコードをいかなる方法でも洗練していません。一部の部分はいくつかの試みの残り物です(たとえば、constructors.getOrNull(0)に関する部分)。私はそれを改善するつもりはありません。

また、この単純なアプローチは、JVMのデフォルトのコンストラクター(it.newInstance()を参照)でのみ機能します。ですから、それは決してマルチプラットフォームのソリューションではありません。

結果は次のようなものです

_bar: default is in use
bar: default is in use
bar: null
bar: null
bar: Java.lang.Object@41906a77
bar: Java.lang.Object@4b9af9a9
bar: Java.lang.Object@4b9af9a9
_

繰り返しますが、これは非常に単純化されているため、本番環境では使用しないでください!

1
dzim