如何扩展Kotlin的枚举?

时间:2019-06-25 08:23:25

标签: kotlin enums

在我的Kotlin项目中,我有一个DefaultError枚举

enum class DefaultError {
    INTERNET_ERROR,
    BLUETOOTH_ERROR,
    TEMPERATURE_ERROR
}

我想扩展它们,以便拥有

enum class NfcAndDefaultError : DefaultError {
    //DefaultError inherited plus
    NFC_ERROR
}

和另一个枚举

enum class KameraAndDefaultError : DefaultError {
    //DefaultError inherited plus
    CAM_ERROR
}

现在,我有

enum class NfcDefaultError {
    INTERNET_ERROR,
    BLUETOOTH_ERROR,
    TEMPERATURE_ERROR,
    NFC_ERROR
}

enum class KameraAndDefaultError {
    INTERNET_ERROR,
    BLUETOOTH_ERROR,
    TEMPERATURE_ERROR,,
    CAM_ERROR
}

我敢打赌Kotlin有一个不错的方式吗?

3 个答案:

答案 0 :(得分:0)

您可以扩展枚举。的种类。但不是继承。枚举可以implement an interface。这意味着要扩展它,您只需添加另一个实现相同接口的枚举即可。

让我们说您有一个错误。该错误具有错误代码。默认错误被实现为DefaultError枚举,可以通过添加实现Error接口的附加枚举来扩展默认错误。

interface Error {
    fun code(): Int
}

enum class DefaultError(private val code: Int) : Error {
    INTERNET_ERROR(1001),
    BLUETOOTH_ERROR(1002),
    TEMPERATURE_ERROR(1003);

    override fun code(): Int {
        return this.code
    }
}

enum class NfcError(private val code: Int) : Error {
    NFC_ERROR(2001);

    override fun code(): Int {
        return this.code
    }
}

enum class KameraError(private val code: Int) : Error {
    CAM_ERROR(3001);

    override fun code(): Int {
        return this.code
    }
}

答案 1 :(得分:0)

简单的答案是,您无法按照您想要的方式扩展Kotlin中的枚举。

我必须同意Miha_x64的评论,即继承是“邪恶的”,仅在合法地有意义的情况下才应使用(是的,在某些情况下,继承是可行的方式)。我相信,除了实际尝试解决Kotlin中的枚举设计外,我们为什么不以其他方式设计解决方案?我的意思是:为什么您首先需要这样的枚举层次结构?有什么好处?为什么不简单地具有一些“常见错误”,而对于任何具体区域需要非常特定的错误的特定错误呢?为什么还要使用枚举?

如果您不愿意使用枚举,那么Januson的解决方案可能是您最好的选择,但是请使用有意义的错误代码,因为使用“ 1001”,“ 1002”,“ 233245”是如此1980年代,阅读和阅读都非常恐怖。与。我发现“ INTERNET_ERROR”和“ BLUETOOTH_ERROR”等含义是神秘的……我们真的不能做得更好,并且更清楚地指出出了什么问题,以便谁读取错误代码都可以真正理解出什么问题而无需挖掘在接下来的数分钟/小时内是通过Internet还是通过大量文档? (当然,除了出于某些合理的原因,代码需要尽可能的小-例如消息大小限制,带宽限制等)。

如果您对使用枚举没有犹豫,则可以考虑以下内容:

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}''."
)

由于上述所有代码本质上都充当静态常量,因此与它们进行比较就像比较枚举(例如:if (yourException.errorCode == NO_INTERNET_CONNECTION) { // do something })一样容易。

确实不需要继承,您真正需要的是将常见错误代码和非常见错误代码清楚地分开。

答案 2 :(得分:0)

不支持枚举继承的原因除了“继承是邪恶的”之外。实际上,这是一个非常实际的原因:

enum class BaseColor { BLUE, GREEN, RED }

val x: BaseColor = ... // must be one of the 3 enums, right?
// e.g. when {} can be done exhaustively with BLUE, GREEN, RED

enum class DerivedColor : BaseColor { YELLOW }

val y: BaseColor = ... // now, it can also be YELLOW
// here, you lose the guarantee that it's a value in a limited set
// and thus the main advantage of enums

有多种选择可以满足您的需求:

1。不同的枚举实现一个通用的界面

我会转介您到this answer

接口是一种非常灵活的解决方案,可让您派生无限数量的枚举。如果这是您需要的,那就去做吧。

2。密封类

在Kotlin中,密封类是枚举的泛化,使您可以在每个值中保留状态。密封类的所有派生类必须预先知道并在同一文件中声明。与接口相比,优点是可以将密封类限制为一组固定的可能类型。例如,这允许您省略else中的when分支。缺点是无法根据设计在sealed class之外添加类型。

在语义上,您有一个枚举枚举:第一个级别确定使用哪种enum class类型,第二个级别确定使用在enum class内部的哪个枚举数(常量)。

enum class DefaultError { INTERNET_ERROR, BLUETOOTH_ERROR, TEMPERATURE_ERROR }
enum class NfcError { NFC_ERROR }
enum class CameraError { CAM_ERROR }

sealed class Error {
    data class Default(val error: DefaultError) : Error()
    data class Nfc(val error: NfcError) : Error()
    data class Camera(val error: CameraError) : Error()
}

fun test() {
    // Note: you can use Error as the abstract base type
    val e: Error = Error.Default(DefaultError.BLUETOOTH_ERROR)

    val str: String = when (e) {
        is Error.Default -> e.error.toString()
        is Error.Nfc -> e.error.toString()
        is Error.Camera -> e.error.toString()
        // no else!
    }
}