Oliver.O

    Oliver.O

    1 year ago
    I have a use-case where I'd like to invoke a generic method from a generic class with and without a type like this: •
    get()
    get<DateModel>()
    Currently, it requires two function definitions to achieve the goal, which might be resolved by introducing a default type for a type parameter as shown in the following example:
    package genericMethodDefaultType
    
    open class Model
    
    open class AttributeModel : Model()
    
    data class DateModel(var value: String) : AttributeModel()
    
    val modelCache = mutableMapOf<Int, Model>()
    
    @Suppress("UNCHECKED_CAST")
    class ModelReference<SpecificModel : Model>(private val modelId: Int) {
        // Variant (1) returning the default type.
        fun get() = modelCache[modelId]!! as SpecificModel
    
        // Variant (2) returning a derivative.
        @JvmName("getSpecific")  // required to disambiguate JVM signatures
        fun <MoreSpecificModel : SpecificModel> get() = modelCache[modelId]!! as MoreSpecificModel
    
        // Variant (3) replacing (1) and (2) – currently not valid Kotlin:
        //     fun <MoreSpecificModel = SpecificModel : SpecificModel> get() = modelCache[modelId]!! as MoreSpecificModel
        //
        //     Note the specification of a default type ` = SpecificModel` which shall be used if type inference cannot
        //     determine a result.
    }
    
    @Suppress("UNUSED_VARIABLE")
    fun main() {
        val modelId = 42
    
        modelCache[modelId] = DateModel("2021-08-18")
    
        val attributeModelReference = ModelReference<AttributeModel>(modelId)
    
        // Some code would access attribute models like this:
        val attributeModel1 = attributeModelReference.get()
        // Does not compile if `get()` variant (1) is not defined: Not enough information to infer type variable MoreSpecificModel
    
        // If `get()` variant (1) is not defined, we would have to specify the type redundantly like this:
        val attributeModel2 = attributeModelReference.get<AttributeModel>()
    
        // A date view might want to access its model like this:
        val dateModel = attributeModelReference.get<DateModel>()
    
        println(attributeModel1)
        println(attributeModel2)
        println(dateModel)
    }
    Is this too exotic or do there exist better solutions for the above case?
    Oliver.O

    Oliver.O

    1 year ago
    Thanks for the reference, I'll add my use case there. Comments still welcome here! 🙂
    elizarov

    elizarov

    1 year ago
    A typical work-around now is to have two different names for those functions. It does not happen that much often in practice, so I cannot say there’s any kind of established convention for it now.
    Oliver.O

    Oliver.O

    1 year ago
    Yes that's also possible. I have used a
    generic
    prefix for the function returning the more generic type. I still prefer the above variants (1) plus (2) as these allow invocations via the same name as if type inference was working as usual.