Map with initial values in abstract class

Tags: ,



I have an abstract class which holds some default values as follows

BaseRequest.kt

abstract class BaseRequest {

    abstract val route: String

    /** Initially, all requests contain the route param, that's why it has to be added for all requests */
    val baseMap: Map<String, String> by lazy { mapOf("route" to route) }

    abstract fun toMap(): Map<String, String>
}

Now, for all subclasses, I need anyone who extends the BaseRequest class to override the toMap() function and return a map that has an initial value of route that was initialized in the base class.

What I mean by that is that once the user overrides the map and fill it in with any values, I add a default value to these values.

For example

ItemRequest.kt

class ItemsRequest: BaseRequest() {

    override val route: String = "items"

    override fun toMap(): Map<String, String>  = mapOf("id" to "7")

}

For the ItemsRequest class, I need the returned map from toMap() function to have the id as well as the default route without having to manually add it in each subclass.

Is this possible?

Answer

I believe you can just simply combine the map that the subclass intends to return with the baseMap variable from the superclass. Since you said from your comments that the original baseMap can be modified, you can simply use the plus operation between baseMap and whatever other map that you want your specific subclass to return like so,

override fun toMap(): Map<String, String>  = baseMap + mapOf("id" to "7")

Note how baseMap is on the left side of the plus operation. In this case, if any keys exist in both baseMap and the map to the right of the plus operation, then the final map returned from toMap will only contain the values from the map on the right side of the plus operation for the keys common to both maps. If you want the baseMap key value pairs to take precedence and always be returned from any map returned from any subclass’ toMap method, then have baseMap on the right side of the plus operation like so,

override fun toMap(): Map<String, String>  = mapOf("id" to "7") + baseMap

However, assuming that you want every subclass’ toMap implementation to return at least the baseMap, it can be pretty redundant to constantly write baseMap + ... or ... + baseMap over and over again for each subclass that overrides this toMap method. Therefore, my suggestion would be to make the toMap function in the parent class a non abstract function and define a different abstract function just for returning a map custom to classes derived from the parent class. Here’s what I mean by that,

abstract class BaseRequest {

    abstract val route: String

    /** Initially, all requests contain the route param, that's why it has to be added for all requests */
    val baseMap: Map<String, String> by lazy { mapOf("route" to route) }

    abstract fun subMap(): Map<String, String>

    // again, up to you with how you want to combine the two maps, 
    // assuming you are okay with key-value pairs being overwritten by one of the maps, 
    // the plus operation is fine
    fun toMap(): Map<String, String> = baseMap + subMap()
}

class ItemsRequest: BaseRequest() {

    override val route: String = "items"

    override fun subMap(): Map<String, String>  = mapOf("id" to "7")

}

Note that the subMap function can also be a variable as well like how you did the route variable. And now, the toMap function for each derived class of BaseRequest will return a combination of baseMap and subMap().



Source: stackoverflow