Как продлить перечисления в Kotlin? - PullRequest
1 голос
/ 25 июня 2019

В моем проекте 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
}

Могу поспорить, у Котлина есть хороший способ?

Ответы [ 3 ]

0 голосов
/ 26 июня 2019

Простой ответ заключается в том, что вы не можете расширять перечисления в Kotlin так, как вам бы хотелось.

Я должен согласиться с комментарием Miha_x64 о том, что наследование является "злом" и должно использоваться только тогда, когда онозаконно имеет смысл (да, все еще существуют ситуации, когда наследование - это путь).Я считаю, что вместо того, чтобы пытаться обойти дизайн перечислений в Kotlin, почему бы не разработать наше решение иначе?Я имею в виду: почему вы думаете, что вам нужна такая иерархия перечислений, для начала?В чем выгода?Почему бы просто не иметь некоторые "общие ошибки", а конкретные ошибки для конкретной области, требующей особых ошибок?Зачем даже использовать перечисления?

Если вы не можете использовать перечисления, то решение Januson может быть вашим лучшим выбором, но, пожалуйста, используйте значимые коды ошибок, потому что "1001", "1002", "233245"и так ужасно читать и работать с ними.Я нахожу "INTERNET_ERROR" и "BLUETOOTH_ERROR" и т. Д. Просто загадочными ... можем ли мы на самом деле не делать ничего лучше и быть более конкретными в отношении того, что пошло не так, чтобы тот, кто читает код ошибки, мог на самом деле понять, что не так, без необходимости копатьчерез интернет или через какую-то здоровенную документацию на следующие несколько минут / часов?(за исключением, конечно, если есть некоторые законные причины, по которым код должен быть как можно меньшего размера - например, ограничения размера сообщения, ограничения полосы пропускания и т. д.)

В случае, если вы не устали использовать перечисления, тогда вы могли бы рассмотреть следующее:

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

Наследование действительно не нужно, действительно нужно четкое разделение между общими и нечастыми кодами ошибок.

0 голосов
/ 26 июня 2019

Существует еще одна причина, по которой наследование enum не поддерживается, чем "наследование это зло".На самом деле, очень практичная причина:

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.Различные перечисления реализуют общий интерфейс

Я хотел бы отослать вас к этот ответ .

Интерфейсы являются очень гибким решением и позволяют вам получать неограниченное количествоперечисления.Если это то, что вам нужно, пойти на это.

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!
    }
}
0 голосов
/ 25 июня 2019

Вы можете расширить Enum.Вид.Но не по наследству.Перечисления могут реализовать интерфейс .Это означает, что для его расширения вы просто добавили бы другое перечисление, реализующее тот же интерфейс.

Допустим, у вас есть ошибка.Эта ошибка имеет код ошибки.Ошибка по умолчанию реализована в виде перечисления DefaultError и может быть расширена путем добавления дополнительных перечислений, реализующих интерфейс ошибки.

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
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...