1

I created a custom Result field (And not Kotlin's Result) in my service so I can return a message field in Both Success & Failure Cases:

sealed class Result<T> {
    data class Success<T>(val value: T, val message: String) : Result<T>()

    data class Failure<T>(val throwable: Throwable? = null, val message: String) : Result<T>() {
        val isExceptional = throwable != null
        val error: Throwable
            get() = throwable ?: error("Error is undefined in [$this]")
    }
}

Then in another class I called a method that produces this result and wanted to log Result.message

logger.info { "Finished with message [${result.message}]." }

Only, kotlin compiler doesn't recognize "message" since it's not a property of Result directly, but a property of Success and Failure.

I tried to override the message field and define it in Result class. But I get an Error.

Error:(10, 38) Kotlin: 'message' in 'Result' is final and cannot be overridden

So, how can one accesss Result.message without casting the result instance to it's derived implementing class (Success or Failure)?

ran8080
  • 41
  • 1
  • 3

3 Answers3

2

A clean solution that I found is the following.

While Kotlin does not allow us to override sealed class members. It does allow us to override interface members.

So, I Created a simple interface for the message field, and implemented it from Result class:

interface ResultMessage {
    val message: String
}

sealed class Result<T> : ResultMessage

// Now I'm able to override the message field simply, with no error
data class Success<T>(val value: T, override val message: String) : Result<T>()

data class Failure<T>(val throwable: Throwable? = null, override val message: String) : Result<T>() {
    val isExceptional = throwable != null
    val error: Throwable
        get() = throwable ?: error("Error is undefined in [$this]")
}
ran8080
  • 41
  • 1
  • 3
  • 3
    "While Kotlin does not allow us to override sealed class members. It does allow us to override interface members." It does allow you to override sealed class members. You just need to mark them `open` or `abstract`, same as in any other class. Your solution is the same as adding an `abstract` member, but with extra steps. – Tenfour04 Feb 28 '21 at 18:21
  • Cool. I think using ```abstract``` here is the nicest approach. – ran8080 Mar 02 '21 at 13:54
2

You can declare an abstract property in the Result class:

sealed class Result<T> {
    abstract val message: String
    data class Success<T>(val value: T, override val message: String) : Result<T>()

    data class Failure<T>(val throwable: Throwable? = null, override val message: String) : Result<T>() {
        val isExceptional = throwable != null
        val error: Throwable
            get() = throwable ?: error("Error is undefined in [$this]")
    }
}

Though, your solution is also an option

Konstantin Raspopov
  • 1,565
  • 1
  • 14
  • 20
1

1- you can mark them open

open val message: String = ""

2- you can define them abstract

abstract val message: String
Ege Kuzubasioglu
  • 5,991
  • 12
  • 49
  • 85