https://kotlinlang.org logo
Title
l

Lawik

04/17/2019, 11:54 AM
Does anyone know whether this is possible using reflection only?
(person::class.companionObjectInstance as PersonDTO.Companion)
(person is an instance of PersonDTO)
r

ribesg

04/17/2019, 12:00 PM
What do you mean by “using reflection only”?
I don’t really know what I’m looking at, but I know that it looks strictly identical to just
PersonDTO.Companion
l

Lawik

04/17/2019, 12:13 PM
I have an interface `Validateable`:
interface Validateable<T> {
    val validator: Validation<T>
}
I implement this interface on my class's companion object:
@Serializable
data class PersonDTO(val id: Long? = null, val name: String, var age: Int) {
    companion object : Validateable<PersonDTO> {
        override val validator = Validation<PersonDTO> {
            PersonDTO::age{
                minimum(0)
            }
        }
    }
}
When processing requests on the backend, I would like to (dynamically) check whether the body's (which is an instance) companion object implements the
Validateable
interface. I have tried this:
val person = PersonDTO(null, "test", -24)
    val companion = person::class.companionObjectInstance
    if(companion is Validateable<*>){
        companion.validator(person)
    }
But this gives me the following error:
Out-projected type 'Validation<out Any?>' prohibits the use of public open operator fun invoke(value: T): ValidationResult<T> defined in io.konform.validation.Validation
It works when I implement the interface on
PersonDTO
instead but I'd like
validator
to be static which doesn't seem possible when overriding from interface 😔
r

ribesg

04/17/2019, 12:35 PM
You should think more Kotlin I guess. But you want to use reflection hmm
You would like to be able to do
anyModelInstance.isValid()
or something like that?
@Lawik Ok I have an idea
I can’t find how to make it work, but I think delegated interface implementation can be useful here
@Lawik would that work? Add an extension to get the validator of a model and that’s it
interface ValidatableModel<This : ValidatableModel<This>>

interface ModelValidator<Model : ValidatableModel<Model>> {
    fun ensureValid(model: Model)
}

data class MyModel(val value: Int) : ValidatableModel<MyModel> {

    object Validator : ModelValidator<MyModel> {

        override fun ensureValid(model: MyModel) {
            check(model.value >= 0)
        }

    }

}
l

Lawik

04/17/2019, 1:23 PM
Sadly that nets me the same error 😔 (when using the following code)
val person = PersonDTO(null, "test", -24)
    val companion = person::class.companionObjectInstance
    if (companion is Validator<*>) {
        companion.validate(person)
    }
r

ribesg

04/17/2019, 1:25 PM
The code you show doesn’t match what I wrote at all
l

Lawik

04/17/2019, 1:26 PM
That's when calling the validate function, this is my model class at the moment:
@Serializable
data class PersonDTO(val id: Long? = null, val name: String, var age: Int) : Validateable<PersonDTO> {
    companion object : Validator<PersonDTO> {
        override fun validate(m: PersonDTO): ValidationResult<PersonDTO> {
            return validator.validate(m)
        }
        
        val validator = Validation<PersonDTO> {
            PersonDTO::age{
                minimum(0)
            }
        }
    }
}
Interfaces:
interface Validateable<This : Validateable<This>>

interface Validator<Model : Validateable<Model>> {
    fun validate(m: Model): ValidationResult<Model>
}
Validation<T>
and
ValidationResult<T>
are from the https://www.konform.io/ library. (Which I'd like to keep using.)
r

ribesg

04/17/2019, 2:03 PM
I think your problem is
if (companion is Validator<*>) {
Not sure how to solve it. But because of that line the validate method can’t be used because T isn’t defined
l

Lawik

04/17/2019, 3:22 PM
I end up doing the following. Interface:
interface Validateable<T>{
   fun validate(): ValidationResult<T>
}
DTO:
@Serializable
data class PersonDTO(val id: Long? = null, val name: String, var age: Int) : Validateable<PersonDTO> {
    override fun validate() = validator.validate(this)

    private companion object {
        val validator = Validation<PersonDTO> {
            PersonDTO::age{
                minimum(0)
            }
        }
    }
}
This allows me to validate my objects as follows:
if (o is Validateable<*>) {
        o.validate()
    }