Home > Enterprise >  Kotlin enforce data class type
Kotlin enforce data class type

Time:07-26

I have various types of events in my application that are represented by Data classes.

All events must have an attribute called EventContent that stores additional data about the event in String format. These events are saved to a database and EventContent is serialized into a JSON via kotlin.x.serialization.

To enforce this, I have used an interface like this on a sample event called ShutdownEvent:

interface Event {
    var eventContent: String
}


data class ShutdownEvent(
    override var eventContent: String
) : Event

Now imagine for the specific event called ShutdownEvent, I know its EventContent must have only 2 attributes: trigger and location, both of which are Strings. How can I enforce that the EventContent follows this structure.

I was hoping for something like this:

data class ShutdownEvent(
    @Serializable(with = ShutdownContentConverter::class) override var eventContent: ShutdownData,
) : Event


@Serializable
data class ShutdownData(
   var trigger: String,
   var location: String,
) 

But this causes problems with my Event interface implementation, because it expects the eventContent attribute to be a String. Is there an easy way to make this work?


Edit:

I would appreciate an answer that allows me to process my events as follows:

fun process(event: Event) {
    if (event is ShutdownEvent) {
        // do something
    }
}

CodePudding user response:

One possible way to deal with this is to make your interface generic in the "event content" type, instead of using String:

interface Event<C> {
    var eventContent: C
}

data class ShutdownEvent(
    @Serializable(with = ShutdownContentConverter::class)
    override var eventContent: ShutdownData,
) : Event<ShutdownData>

@Serializable
data class ShutdownData(
   var trigger: String,
   var location: String,
)

If all possible types of event content have some common properties, you can define a parent EventContent interface like this:

/** A parent interface for all types of event content. */
interface EventContent {
    val someCommonProp: String
}

interface Event<C : EventContent> {
    var eventContent: C
}

data class ShutdownEvent(
    @Serializable(with = ShutdownContentConverter::class)
    override var eventContent: ShutdownData,
) : Event<ShutdownData>

@Serializable
data class ShutdownData(
    override val someCommonProp: String,
    var trigger: String,
    var location: String,
) : EventContent

Side note: I would strongly advise to use val instead of var everywhere here. You probably don't want event data to be mutable. Once the event is created no-one should really modify it.

  • Related