I have a class with constructor declared like this
class Facade<T : SuperClass>(
val kClass: KClass<in T> = SuperClass::class
)
It is done like this so that developer doesn't have to specify SuperClass if they want to use it instead of a subclass. Reason to even send the class type is, so that developer doesn't have to specify type in angle brackets .
But now comes the problem. Creating instance like below, says that there is not enough information to infer parameter T. Which is resulting in having to put class into angle brackets.
Facade()
But since default value is SuperClass, then kotlin should be able to infer parameter T as SuperClass. What am I thinking wrong?
Thank you
TL;DR:
Facade(SubClass:class) // working
Facade(SuperClass:class) // working, but don't want (superclass is default)
Facade<SuperClass>() // working, but don't want angle brackets <>
Facade() // not working, cannot infer T type from default, why?
For the Facade()
to be equivalent to Facade(DerivedClass::class)
the default constructor parameter would have to be declared as val kClass: KClass<in T> = T::class
. However to use T::class
the T
type parameter would need to be reified
. A type parameter can only be reified in inline functions and not constructors.
To workaround this issue you can declare a factory function that delegates to constructor like so:
inline fun <reified T : SuperClass> Facade(): Facade<T> = Facade(T::class)
This allows one to use it as e.g.:
val derivedFacade:Facade<DerivedClass> = Facade()
Note that if you would like to use the SuperClass
as the default parameter for T
you would need to declare another factory method using different name e.g.:
fun SuperFacade(): Facade<SuperClass> = Facade()
This is required since if we had declared @JvmName("SuperFacade") fun Facade() = Facade(SuperClass::class)
the compile would match it whenever we do not provide type parameters. This in turn would defy the type inference from the derivedFacade
example.