Как я могу обобщить функции в классе enum в Kotlin?

#android #kotlin #enums

#Android #kotlin #перечисления

Вопрос:

Как я могу создать класс, который можно было бы более повторно использовать с классами 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 amp;amp; string != null) {
                try {
                    return enumValueOf<T>(
                        string.trim()
                            .toUpperCase(Locale.getDefault()).replace(" ", "_")
                    )
                } catch (e: IllegalArgumentException) {
                    Log.e(TAG, e.message)
                }
            }
            return null
        }
    }
}
  

Ответ №1:

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

Это поможет:

 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 следующим образом:

 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 Интерфейс теперь можно использовать повторно, просто имея сопутствующий объект enum, реализующий его.

Комментарии:

1. Ты легенда! Спасибо, сэр.

Ответ №2:

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

 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 метод таким образом.

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

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

что неплохо, если только количество способов оплаты не ограничено, но не совсем желательно. Мы можем удалить это коварное ключевое слово if or 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 методе, который я продемонстрировал следующим образом:

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

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

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

Ответ №3:

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

 fun fromString(type: String): PaymentMethodType? = PaymentMethodType.values().find { it.type.toLowerCase() == type.toLowerCase() }