Hi all, I am attempting to make a generic abstract...
# announcements
j
Hi all, I am attempting to make a generic abstract class for Plugin's whose subclasses can enforce strongly typed method parameters. I was able to get this mostly working using the following but have hit issues when trying to iterate over the concrete Plugin subclasses in a generic manner. Here is a short(ish) sample of what I am attempting. Any suggestions on how to approach this? Thank you in advance!
Copy code
// BASE GENERIC CLASSES
open class PluginCallOptions: HashMap<String, Any?>()
open class CallOptions: HashMap<String, PluginCallOptions>()

abstract class Plugin<I: PluginCallOptions, G: PluginCallOptions>(
    private val id: String
) {
    fun id(): String {
        return id
    }
    open fun identify(userId: String?, options: I? = null) {
        println("identify $id $options")
    }
    open fun group(userId: String?, options: G? = null) {
        println("group $id $options")
    }
}

// PLUGIN SUBCLASSES
class MyIdentifyOptions(val iProp: String): PluginCallOptions()
class MyGroupOptions(val gProp: String): PluginCallOptions()
class MyPlugin: Plugin<MyIdentifyOptions, MyGroupOptions>("myPlugin") {
    override fun identify(userId: String?, options: MyIdentifyOptions?) {
        super.identify(userId, options)
    }

    override fun group(userId: String?, options: MyGroupOptions?) {
        super.group(userId, options)
    }
}

class MyOtherIdentifyOptions(val iProp: String): PluginCallOptions()
class MyOtherGroupOptions(val gProp: String): PluginCallOptions()
class MyOtherPlugin: Plugin<MyOtherIdentifyOptions, MyOtherGroupOptions>("myOtherPlugin") {
    override fun identify(userId: String?, options: MyOtherIdentifyOptions?) {
        super.identify(userId, options)
    }

    override fun group(userId: String?, options: MyOtherGroupOptions?) {
        super.group(userId, options)
    }
}

class IdentifyOptions(
    var myPlugin: MyIdentifyOptions? = null,
    var myOtherPlugin: MyOtherIdentifyOptions? = null,
): CallOptions()

class GroupOptions(
    var myPlugin: MyGroupOptions? = null,
    var myOtherPlugin: MyOtherGroupOptions? = null,
): CallOptions()

class Iterator(val plugins: List<Plugin<out PluginCallOptions, out PluginCallOptions>>) {
    fun identify(userId: String, callOptions: IdentifyOptions) {
        // ERROR here
        iterate { plugin -> plugin.identify(userId, callOptions.get(plugin.id())) }
    }

    fun group(userId: String, callOptions: GroupOptions) {
        // ERROR here
        iterate { plugin -> plugin.group(userId, callOptions.get(plugin.id())) }
    }

    fun iterate(method: (plugin: Plugin<out PluginCallOptions, out PluginCallOptions>) -> Unit) {
        plugins.forEach {
            method(it)
        }
    }
}

// EXAMPLE USAGE
val iter = Iterator(plugins = arrayListOf(MyPlugin(), MyOtherPlugin()))

iter.identify("user-id", IdentifyOptions(
    myPlugin = MyIdentifyOptions(iProp = "Identify value")
))

iter.group("user-id", GroupOptions(
    myOtherPlugin = MyOtherGroupOptions(gProp = "Group value")
))
image.png
I also tried using
in
instead but then I need to typecast the values in the
plugins
array and the PluginCallOptions values never make it to the underlying Plugins.
Copy code
// BASE GENERIC CLASSES
open class PluginCallOptions: HashMap<String, Any?>()
open class CallOptions: HashMap<String, PluginCallOptions>()

abstract class Plugin<in I: PluginCallOptions, in G: PluginCallOptions>(
    private val id: String
) {
    fun id(): String {
        return id
    }
    open fun identify(userId: String?, options: I? = null) {
        println("identify $id $options")
    }
    open fun group(userId: String?, options: G? = null) {
        println("group $id $options")
    }
}

// PLUGIN SUBCLASSES
class MyIdentifyOptions(val iProp: String): PluginCallOptions()
class MyGroupOptions(val gProp: String): PluginCallOptions()
class MyPlugin: Plugin<MyIdentifyOptions, MyGroupOptions>("myPlugin") {
    override fun identify(userId: String?, options: MyIdentifyOptions?) {
        super.identify(userId, options)
    }

    override fun group(userId: String?, options: MyGroupOptions?) {
        super.group(userId, options)
    }
}

class MyOtherIdentifyOptions(val iProp: String): PluginCallOptions()
class MyOtherGroupOptions(val gProp: String): PluginCallOptions()
class MyOtherPlugin: Plugin<MyOtherIdentifyOptions, MyOtherGroupOptions>("myOtherPlugin") {
    override fun identify(userId: String?, options: MyOtherIdentifyOptions?) {
        super.identify(userId, options)
    }

    override fun group(userId: String?, options: MyOtherGroupOptions?) {
        super.group(userId, options)
    }
}

class IdentifyOptions(
    var myPlugin: MyIdentifyOptions? = null,
    var myOtherPlugin: MyOtherIdentifyOptions? = null,
): CallOptions()

class GroupOptions(
    var myPlugin: MyGroupOptions? = null,
    var myOtherPlugin: MyOtherGroupOptions? = null,
): CallOptions()

class Iterator(val plugins: List<Plugin<in PluginCallOptions, in PluginCallOptions>>) {
    fun identify(userId: String, callOptions: IdentifyOptions) {
        iterate { plugin -> plugin.identify(userId, callOptions.get(plugin.id())) }
    }

    fun group(userId: String, callOptions: GroupOptions) {
        iterate { plugin -> plugin.group(userId, callOptions.get(plugin.id())) }
    }

    fun iterate(method: (plugin: Plugin<in PluginCallOptions, in PluginCallOptions>) -> Unit) {
        plugins.forEach {
            method(it)
        }
    }
}

val iter = Iterator(plugins = arrayListOf(
    MyPlugin() as Plugin<PluginCallOptions, PluginCallOptions>,
    MyOtherPlugin() as Plugin<PluginCallOptions, PluginCallOptions>
))

iter.identify("user-id", IdentifyOptions(
    myPlugin = MyIdentifyOptions(iProp = "Identify value")
))

iter.group("user-id", GroupOptions(
    myOtherPlugin = MyOtherGroupOptions(gProp = "Group value")
))