The simple answer is that you can't extend enums in Kotlin the way you would want to.
I have to agree with Miha_x64's comment stating that inheritance is "evil" and should only be used when it legitimately makes sense (yes, there are still situations when inheritance is the way to go). I believe that instead of actually trying to work around the design of enums in Kotlin, why don't we design our solution differently? I mean: Why do you think you need such an enum hierarchy, to begin with? What's the benefit? Why not simply have some "common errors" instead and specific errors for whichever concrete area needs very specific errors? Why even use enums?
If you are dead set on using enums, then Januson's solution might be your best bet, but do please use meaningful error codes because using "1001", "1002", "233245" is so 1980s and utterly horrible to read and work with. I find "INTERNET_ERROR" and "BLUETOOTH_ERROR", etc. just as cryptic... can we really not do any better and be more specific about what went wrong so that whoever reads the error code CAN actually understand what is wrong without needing to dig through the internet or through some hefty documentation for the next many minutes/hours? (except, of course, if there are some legitimate reasons why the code needs to be as small as possible - e.g. message size limitations, bandwidth limitations, etc.)
In case you are not dead set on using enums, then you could consider the following:
data class ErrorCode(
val code: String,
val localeKey: String,
val defaultMessageTemplate: String
)
val TENANT_ACCESS_FORBIDDEN = ErrorCode(
"TENANT_ACCESS_FORBIDDEN",
"CommonErrorCodes.TENANT_ACCESS_FORBIDDEN",
"Not enough permissions to access tenant ''{0}''."
)
val NO_INTERNET_CONNETION = ErrorCode(
"NO_INTERNET_CONNETION",
"DeviceErrorCodes.NO_INTERNET_CONNETION",
"No internet connection."
)
val NO_BLUETOOTH_CONNECTION = ErrorCode(
"NO_BLUETOOTH_CONNECTION",
"DeviceErrorCodes.NO_BLUETOOTH_CONNECTION",
"No bluetooth connection."
)
val TEMPERATURE_THRESHOLD_EXCEEDED = ErrorCode(
"TEMPERATURE_THRESHOLD_EXCEEDED",
"DeviceErrorCodes.TEMPERATURE_THRESHOLD_EXCEEDED",
"Temperature ''{0}'' exceeds the maximum threshold value of ''{1}''."
)
Because all the above codes, in essence, act as static constants, comparing against them is as easy as comparing enums (e.g.: if (yourException.errorCode == NO_INTERNET_CONNECTION) { // do something }
).
Inheritance is really not needed, what you really need is a clear separation between common and non-common error codes.