0

I have a class that calls functions depending on events. Events are emitted from sockets. I should catch these events, parse JSON and respond (call a corresponding function). For instance, {"event_name": "message", "data": {"text": "dfgfdgfdg", "sender": "dsfdsfs"}}

fun listener(jsonString: String, methodsMap: Map<String, () -> Unit>) {
    val json = JSONObject(jsonString)
    val data = json.getJSONObject("data")
    when (json.get("event_name")) {
        "update" -> {
            val count = data.getInt("count")
            methodsMap["update"]?.invoke(count) // 1 parameter.
        }
        "message" -> {
            val message = data.getString("text")
            val sender = data.getString("sender")
            methodsMap["message"]?.invoke(message, sender) // 2 parameters.
        }
    }
}

So, I cannot create one method that calls functions with different parameters. How to do this?

4
  • If the type of your functions is () -> Unit why do you invoke them with parameters? Commented Nov 3, 2020 at 7:47
  • @DavidSoroko, that was an example. Commented Nov 3, 2020 at 8:27
  • "functions with different parameters" means different amount of parameters and different type? or just an amount. Commented Nov 3, 2020 at 8:44
  • @BenShmuel, all functions may have different amount and type of parameters. For instance, the third function will have Int, Int, the fourth - String, Int, String. Commented Nov 3, 2020 at 9:04

2 Answers 2

2

Since you are already have if-then logic in listener, having the functions in a Map is of questionable value and it forces you to to deal with the fact that your functions are of different types. If it is parametrisation of listener you are after, perhaps this (simplified example code that skips JSON) is sufficient:

class UpdateHandler {
    fun update(n: Int) = println("update ( $n )")
}
class MessageHandler {
    fun message(s1: String, s2: String) = println("message ( $s1 $s2 )")
}

fun listener(jsonString: String, updateF: (Int) -> Unit, messageF: (String, String) -> Unit) {
    when (jsonString) {
        "update" -> updateF(73)
        "message" -> messageF("message", "sender")
    }
}

fun main() {
    val updateHandler = UpdateHandler()
    val messageHandler = MessageHandler()

    val listener = { json: String -> listener(json, updateHandler::update, messageHandler::message) }
    listener("update") // prints: update ( 73 )
    listener("message")// prints: message ( message sender )
}

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks, this is a good variant, but listener parameters may change when we add or edit events. So, every adding a new event will lead to changes in listener. I want to have single collection of functions, that should be changed outside of listener.
Even with all functions in a single map, you will need to change the listener code when you add/remove functions, since you are parsing the parameters before specific function invocation. I guess you could have several maps (by function type) , something like processIntMap and processMessageMap. This may or may not make sense in the context of your application
A good variant. A problem is I want to have public class with listener that will change if events change. And functions won't belong to that class, they should be passed to the listener method from other classes. So, they shouldn't be written near listener, but exist only in other classes. That's why I want to call listener and pass a list of methods to it. All JSON parsing logic should stay inside listener. I want to pass only callbacks.
I updated my answer so that "they shouldn't be written near listener, but exist only in other classes." . Fundamentally though, this is the same answer as before
0

First, I wanted to use a list of parameters in each function, but it leads to poor type verification during compilation. Also I wanted to assign vararg instead of List, but couldn't.

fun listener(jsonString: String, methodsMap: Map<String, (List<Any>) -> Unit>) {
    ...
    methodsMap["update"]?.invoke(listOf(count)) // 1 parameter.
    ...
    methodsMap["message"]?.invoke(listOf(message, sender)) // 2 parameters.
}

This is a poor solution. Bugs may occur, we should remember to change methodsMap in every class that uses listener when we change any event.

Second, I tried to use sealed classes. This is not so simple.

Third, I tried to use interface. We know that callbacks are usually made with interfaces. We can even merge interfaces in Kotlin. So, this can be a solution to a problem (but not to a question).

fun listener(jsonString: String, callback: EventListener) {
    val json = JSONObject(jsonString)
    val data = json.getJSONObject("data")
    when (json.get("event_name")) {
        "update" -> {
            val count = data.getInt("count")
            callback.onUpdate(count)
        }
        "message" -> {
            val text = data.getString("text")
            val sender = data.getString("sender")
            callback.onNewMessage(text, sender)
        }
    }
}

interface EventListener {
    fun onUpdate(count: Int)
    fun onNewMessage(text: String, sender: String)
}

Then we can call listener outside of the class and pass any callbacks we like.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.