Как я могу обобщить функции на классе перечисления в Kotlin? - PullRequest
1 голос
/ 06 марта 2019

Как я могу создать класс, который можно было бы использовать повторно с enum-классами, так как позже у меня может появиться еще несколько классов? Моя цель - сделать его более гибким и универсальным для другого использования.

enum class PaymentMethodType(val type: String) {

    PAYPAL("Paypal"),
    VISA("Visa"),
    MASTERCARD("MasterCard"),
    VISA_DEBIT("VISA Debit"),
    LPQ_CREDIT("Lpq Credit");

    companion object {

        private val TAG: String = this::class.java.simpleName

        fun fromString(name: String): PaymentMethodType? {
            return getEnumFromString(PaymentMethodType::class.java, name)
        }

        private inline fun <reified T : Enum<T>> getEnumFromString(c: Class<T>?, string: String?): T? {
            if (c != null && string != null) {
                try {
                    return enumValueOf<T>(
                        string.trim()
                            .toUpperCase(Locale.getDefault()).replace(" ", "_")
                    )
                } catch (e: IllegalArgumentException) {
                    Log.e(TAG, e.message)
                }
            }
            return null
        }
    }
}

Ответы [ 3 ]

1 голос
/ 06 марта 2019

Получите все значения перечисления с помощью PaymentMethodType.values(), затем используйте find(), чтобы получить то, что вам нужно:

fun fromString(type: String): PaymentMethodType? = PaymentMethodType.values().find { it.type.toLowerCase() == type.toLowerCase() }
1 голос
/ 06 марта 2019

Вы можете обобщить вашу функцию getEnumFromString, создав интерфейс и реализовав его для объекта-компаньона.Расширение в этом интерфейсе позволит вам вызывать функцию непосредственно в компаньоне вашего перечислимого класса.

Это поможет:

interface EnumWithKey<T : Enum<T>, K> {
    val T.key: K
}

/* The reified type parameter lets you call the function without explicitly 
 * passing the Class-object.
 */
inline fun <reified T : Enum<T>, K> EnumWithKey<T, K>.getByKey(key: K): T? {
    return enumValues<T>().find { it.key == key }
}

Теперь вы можете создать свой PaymentMethodType какthis:

enum class PaymentMethodType(val type: String) {
    PAYPAL("Paypal"),
    VISA("Visa"),
    MASTERCARD("MasterCard"),
    VISA_DEBIT("VISA Debit"),
    LPQ_CREDIT("Lpq Credit");

    companion object : EnumWithKey<PaymentMethodType, String> {
        // Just define what the key is
        override val PaymentMethodType.key
            get() = type
    }
}

И вуаля, теперь вы можете сделать это:

println(PaymentMethodType.getByKey("Paypal")) // Prints PAYPAL

Интерфейс EnumWithKey теперь можно повторно использовать, просто реализуя его с помощью объекта-компаньона перечисления.

0 голосов
/ 06 марта 2019

Ну?Как насчет этого кода?

enum class PaymentMethodType(val type: String) {
    PAYPAL("Paypal"),
    VISA("Visa"),
    MASTERCARD("MasterCard"),
    VISA_DEBIT("VISA Debit"),
    LPQ_CREDIT("Lpq Credit");

    companion object {
        private val TAG: String = PaymentMethodType::class.simpleName

        fun fromString(name: String?): PaymentMethodType? {
            val maybeType = PaymentMethodType.values().firstOrNull { it.type == name }
            if (maybeType == null) {
                Log.e(TAG, "No corresponding PaymentMethodType for $name")
            }
            return maybeType
        }
    }
}

Просто сделанный метод getEnumFromString проще, как этот способ.

Более того, если вы хотите сделать свой PaymentMethodType более "многоразовым, гибким и глобальным", добавьте некоторый абстрактный метод в вашPaymentMethodType или рассмотрите возможность использования Запечатанный класс в этом случае.Мы можем догадаться, что для многих способов оплаты требуются свои собственные протоколы, а для реализации этого с помощью enum требуется внешняя ветвь when или if-else.Например, код должен выглядеть следующим образом:

fun paymentProcessor(payment: PaymentMethodType): Boolean {
    return when (payment) {
        PAYPAL -> { processPaypalPayment() }
        VISA   -> { processVisaPayment() }
        // ...
    }
}

, что неплохо, если количество способов оплаты не ограничено, но не совсем желательно.Мы можем удалить это коварное ключевое слово if или when следующим образом (сохраняя подход enum class):

enum class PaymentMethodType(val type: String) {
    PAYPAL("Paypal") {
        override fun processPayment(): Boolean {
            TODO("Not implemented.")
        }
    },
    VISA("Visa") {
        override fun processPayment(): Boolean {
            TODO("Not implemented.")
        }
    },
    // ... more types ...
    ;

    abstract fun processPayment(): Boolean

    // ...
}

При любом подходе мы можем исключить ключевое слово when в paymentProcessor методе Iдемонстрируется так:

fun paymentProcessor(payment: PaymentMethodType): Boolean {
    return payment.processPayment()
}

Я не объясняю sealed class подход, так как код в этом случае не сильно отличается от подхода enum class. Официальный документ может помочь.

Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...