Hi all, I have no idea how to do the following : I...
# announcements
g
Hi all, I have no idea how to do the following : I have a few classes
A
,
B
,
C
, ... (for our use-case, they are describing different type of Message) I have a function
f
defined by
f(msg: A) : AP
,
f(msg: B) : BP
,
f(msg: C) : CP
... that convert those message into another format (Avro classes) My issue is that I have no way to apply this function to an arbitrary collection of
A
,
B
,
C
, ..
setOf(msgA, msgB, msgA, msgC, msgB..).forEach { f(it) }
because
it
is then of type
Any
(despite each
f(it)
individually is valid, the syntax is invalid) Would be happy to receive suggestion 🙂 about how to handle this
a
Do a when check (just a suggesstion), it is more like boilerplate ;(
g
Yes thx. Actually I wanted to avoid that. Not just because it's a lot of boilerplate, but also because I want to avoid to forget some cases..)
s
Why not make a sealed class and make all the message types as subclasses for it. Then make each f(x) a member function of respective classes, you can directly call then right?
m
Yes, I support Sandesh solution: either make A,B,C… into a sealed class hierarchy or implement a common interface with the
f()
function. In the end polymorphism is what will save you from ifs/when
g
Thx. I did that and it works well. Still, it's strange to be forced to write such code
Copy code
fun convertToAvro(message: Message): SpecificRecordBase = when (message) {
    is CancelJob -> convertToAvro(message)
    is DispatchJob -> convertToAvro(message)
    is JobAttemptCompleted -> convertToAvro(message)
    is JobAttemptDispatched -> convertToAvro(message)
    is JobAttemptFailed -> convertToAvro(message)
    is JobAttemptStarted -> convertToAvro(message)
    is JobCanceled -> convertToAvro(message)
    is JobCompleted -> convertToAvro(message)
    is JobCreated -> convertToAvro(message)
    is JobDispatched -> convertToAvro(message)
    is JobStatusUpdated -> convertToAvro(message)
    is RetryJob -> convertToAvro(message)
    is RetryJobAttempt -> convertToAvro(message)
    is RunJob -> convertToAvro(message)
}
(Message is a sealed class here)
s
Oh I see lot of message types.. in that case no idea :(
Why not make an abstract class MessageType with an abstract method convertToAvro, and instead of when statement, use
Copy code
(message as MessageType).convertToAvro
👏 1
☝️ 2
💯 1
g
@Sandesh Baliga sorry, I do not get it.
a
Make an abstract function in Message sealed class, and then implement it on every subclass
And this function will call that
g
ok. thx. Actually it gives the idea to use extension:
Copy code
fun convertToAvro(message: Message): SpecificRecordBase = message.test()

    fun Message.test() : SpecificRecordBase = convertToAvro(this)
apparently it's enough 🙂
👍 1
Sorry the extension is not valid - i though it was but not. It leads to a stackoverflow due to recursive calls
m
Yeah it’s an infinite recursion…it won’t get you anywhere. It seems you’re not familiar with polymorphism, so I’ll show you a couple of solutions you can employ to eliminate that ugly when, which is hardly maintainable. Use a common interface
Copy code
interface Message {
    fun convertToAvro(): SpecificRecordBase
}

class CancelJob : Message {
    override fun convertToAvro(): SpecificRecordBase {
        TODO("create avro record")
    }
}

class DispatchJob : Message {
    override fun convertToAvro(): SpecificRecordBase {
        TODO("create avro record")
    }
}

// and so on...
or Use a sealed class
Copy code
sealed class Message {
    abstract fun convertToAvro(): SpecificRecordBase

    class CancelJob : Message() {
        override fun convertToAvro(): SpecificRecordBase {
            TODO("create avro record")
        }
    }

    class DispatchJob : Message() {
        override fun convertToAvro(): SpecificRecordBase {
            TODO("create avro record")
        }
    }
    // and so on...
}
then in any case you can convert various message types in the same collection:
Copy code
setOf<Message>(...).forEach { it.convertToAvro() } // polymorphic call is dynamically dispatched to runtime type
In this way, whenever you add a new Message subtype the client code above won’t break.
g
Thx a lot @Matteo Mirk . What if
Message
is not under my control (from another library) but
convertToAvro
is ?
👍 1
m
Oh I see… so the problem is a little harder. You could try to solve it with an extension function but unfortunately they’re resolved statically on the extension type, so they’re not polymorphic: https://kotlinlang.org/docs/reference/extensions.html#extensions-are-resolved-statically Right now I can’t find a solution from the top of my head…
g
ok thx anyway @Matteo Mirk it was helpful
👌 1