如何在Kotlin中的枚举类上泛化函数?

时间:2019-03-06 08:52:00

标签: android kotlin enums

如何创建一个可以在枚举类中重用的类,因为以后可能还会有几个类?我的观点是使它更可重用,灵活并且在其他用途​​上具有全局性。

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 个答案:

答案 0 :(得分: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接口现在可以重新使用,只需让枚举的同伴对象实现即可。

答案 1 :(得分:1)

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

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

答案 2 :(得分:0)

好吗?这个代码怎么样?

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

    // ...
}

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

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

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

希望这会有所帮助。