Helio
05/12/2021, 2:45 AMephemient
05/12/2021, 3:45 AMout ModelBase
means "any subtype of ModelBase". which means you don't know if isValid()
accepts a `model: ModelBase`: it may only accept some (unknown) subtype. the only type which is guaranteed to be a subtype of everything is Nothing
, and so that is the only type that is legal to pass to Validator<out ModelBase>::isValid
.ephemient
05/12/2021, 3:46 AMinterface Validator<in T : ModelBase>
fun <T : ModelBase> getValidator(model: T): Validator<T>
variance (not covariance) at the declaration (not use) siteephemient
05/12/2021, 3:47 AMT
is an input, it should be in
Helio
05/12/2021, 3:48 AMephemient
05/12/2021, 3:48 AMgetValidator
method cannot be typecheckedephemient
05/12/2021, 3:49 AMHelio
05/12/2021, 3:49 AMHelio
05/12/2021, 3:51 AMin
Helio
05/12/2021, 3:51 AMType mismatch.
Required:
Validator<ModelBase>
Found:
ModelAValidator
ephemient
05/12/2021, 3:51 AMHelio
05/12/2021, 3:53 AMephemient
05/12/2021, 3:53 AMephemient
05/12/2021, 3:54 AMsealed class ModelBase {
abstract fun getValidator(): Validator<ModelBase>
}
data class ModelA(...) : ModelBase() {
override fun getValidator(): Validator<ModelA> = ModelAValidator
}
data class ModelB(...) : ModelBase() {
override fun getValidator(): Validator<ModelB> = ModelBValidator
}
then everything will typecheck, although this limits your uses somewhatHelio
05/12/2021, 3:54 AMHelio
05/12/2021, 3:55 AMephemient
05/12/2021, 3:55 AM<T : ModelBase> T.getValidator()
returns a Validator<T>
- it could return a Validator<some other subtype of ModelBase>
ephemient
05/12/2021, 3:56 AMModelA
and ModelB
, those ones will work... if you know their types staticallyephemient
05/12/2021, 3:57 AMfun <T : ModelBase> getValidator(model: T): Validator<T> {
when (model) {
is ModelA -> ModelAValidator as Validator<T>
is ModelB -> ModelBValidator as Validator<T>
it is unsafe for good reason, though.ephemient
05/12/2021, 3:58 AMval modelA: ModelBase = ModelA(...)
val validator: Validator<ModelBase> = getValidator(modelA)
validator.isValid(ModelB(...))
which will typecheck, but fail at runtimeHelio
05/12/2021, 3:58 AMephemient
05/12/2021, 3:58 AMHelio
05/12/2021, 3:59 AMephemient
05/12/2021, 3:59 AMephemient
05/12/2021, 4:00 AMdata class ModelA(...) {
companion object {
val validator: Validator<ModelA> = ...
}
}
ephemient
05/12/2021, 4:01 AMModelBase.getValidator()
that way, but there isn't a great way to do that anywayHelio
05/12/2021, 4:02 AMephemient
05/12/2021, 4:02 AMHelio
05/12/2021, 4:03 AMHelio
05/12/2021, 4:03 AMephemient
05/12/2021, 4:04 AMValidator<T>
occurs in out
positionephemient
05/12/2021, 4:04 AMephemient
05/12/2021, 4:06 AMinterface Validator {
fun isValid(model: ModelBase)
}
then it would be possible, ModelAValidator::isValid
would just have to test that model is ModelA
first, etc. but that loses type safety in a different way, so may not be an ideal solution eitherHelio
05/12/2021, 4:09 AMwhen(model) {
is ModelA -> //DoBlah
....
}
The real interface I’m implementing has around 5 methods. It would not be ideal to have these checks.Albert Chang
05/12/2021, 4:57 AM@Suppress("UNCHECKED_CAST")
to suppress the warning. It is also used in Kotlin stdlib, such as here.ephemient
05/12/2021, 5:47 AMephemient
05/12/2021, 5:48 AMHelio
05/12/2021, 5:55 AMephemient
05/12/2021, 7:46 AM