【问题标题】:How can I generalize functions on an enum class in Kotlin?如何在 Kotlin 的枚举类上泛化函数?
【发布时间】:2019-03-06 08:51:59
【问题描述】:

我怎样才能创建一个可以通过枚举类更可重用的类,因为我以后可能会有更多的类?我的观点是让它更可重用、更灵活、更全球化,以供其他用途使用。

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

【问题讨论】:

    标签: android kotlin enums


    【解决方案1】:

    您可以通过创建一个接口并让您的伴随对象实现它来概括您的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

    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 接口现在可以通过让枚举的伴随对象实现它来重用。

    【讨论】:

    • 你是传奇!谢谢先生。
    【解决方案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 方法更简单。

    此外,如果您想让您的 PaymentMethodType 更加“可重用、灵活和全局”,请在您的 PaymentMethodType 上添加一些抽象方法,或者在这种情况下考虑使用 Sealed 类。我们可以猜测,许多支付方式都需要自己的协议,而通过enum 实现它需要外部化的whenif-else 分支来执行此操作。例如,代码应该是这样的:

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

    这还不错,除非付款方式的数量有限但不太理想。我们可以像这样删除这个阴险的ifwhen 关键字(保留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
    
        // ...
    }
    

    无论使用哪种方法,我们都可以在我演示的paymentProcessor 方法中消除when 关键字,如下所示:

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

    我不解释sealed class 方法,因为在这种情况下,代码与enum class 方法相比没有太大区别。 The official document 可能会有所帮助。

    希望这会有所帮助。

    【讨论】:

      【解决方案3】:

      使用PaymentMethodType.values() 获取所有枚举值,然后使用find() 获取您需要的值:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-10-08
        • 1970-01-01
        • 2021-07-20
        • 1970-01-01
        • 2017-02-03
        相关资源
        最近更新 更多