Although I'd rather not use that approach in production, you could do something like I've done in the following snippet:
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)
}
Note, that I have not polished the code in any way. Some parts are leftovers from several attempts (e.g. the part about the constructors.getOrNull(0)
) and I don't intend to improve that.
Also: This simple approach only works with default constructors (see it.newInstance()
) on the JVM. So that's no multi-platform solution in any way.
The result is something like
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
Again: Keep in mind, this is very simplistic, don't use that in production!