I'm trying to come up with a data model that allows me to do the following:
- Define a type of
Task
and change itsStatus
- This
Status
can be eitherInProgress
orCompleted
- In the case of a completed
Status
, I want to be able to add data, that is specific to theTask
that was completed.
Initially, I came up with this data model:
sealed class Task<R : TaskResult> {
abstract val status: TaskStatus<R>
data class A(
val data: String,
override val status: TaskStatus<NoResult>,
) : Task<NoResult>()
data class B(
val data: String,
override val status: TaskStatus<TaskBResult>,
) : Task<TaskBResult>()
}
sealed class TaskStatus<R : TaskResult> {
object InProgress : TaskStatus<NoResult>()
data class Completed<R : TaskResult>(val result: R) : TaskStatus<R>()
}
sealed class TaskResult {
object NoResult : TaskResult()
data class TaskBResult(val resultData: String) : TaskResult()
}
Here you have Task.A
and Task.B
, where:
- A completed
Task.A
only acceptsNoResult
- A completed
Task.B
only acceptsTaskBResult
However, when I run this:
fun main() {
val taskA = Task.A(
data = "data",
status = TaskStatus.InProgress
).copy(
status = TaskStatus.Completed(
result = NoResult
)
)
val taskB = Task.B(
data = "data",
status = TaskStatus.InProgress
).copy(
status = TaskStatus.Completed(
result = TaskBResult(
resultData = "resultData"
)
)
)
}
I get the following compile error for setting the initial status of Task.B
:
status = TaskStatus.InProgress
Type mismatch.
Required: TaskStatus<TaskResult.TaskBResult>
Found: TaskStatus.InProgress
Does anyone know how to change the data model so I'm allowed to run this (or a very similar) main
function?
CodePudding user response:
This could work with a very little change: just make TaskStatus
a covariant generic class and make InProgress
a TaskStatus<Nothing>
. This is a typical strategy you can use when you have "special case" objects that represent no state. After this change, your code should compile:
sealed class Task<R : TaskResult> {
abstract val status: TaskStatus<R>
data class A(
val data: String,
override val status: TaskStatus<TaskResult.NoResult>,
) : Task<TaskResult.NoResult>()
data class B(
val data: String,
override val status: TaskStatus<TaskResult.TaskBResult>,
) : Task<TaskResult.TaskBResult>()
}
sealed class TaskStatus<out R : TaskResult> {
object InProgress : TaskStatus<Nothing>()
data class Completed<R : TaskResult>(val result: R) : TaskStatus<R>()
}
sealed class TaskResult {
object NoResult : TaskResult()
data class TaskBResult(val resultData: String) : TaskResult()
}
fun main() {
val taskA = Task.A(
data = "data",
status = TaskStatus.InProgress
).copy(
status = TaskStatus.Completed(
result = NoResult
)
)
val taskB = Task.B(
data = "data",
status = TaskStatus.InProgress
).copy(
status = TaskStatus.Completed(
result = TaskBResult(
resultData = "resultData"
)
)
)
}