【问题标题】:Kotlin: How to extend the enum class with an extension functionKotlin:如何使用扩展函数扩展枚举类
【发布时间】:2016-03-13 04:24:45
【问题描述】:

我正在尝试使用以下函数扩展 String 类型的枚举类,但无法像这样在调用站点使用它:

fun <T: Enum<String>> Class<T>.join(skipFirst: Int = 0, skipLast: Int = 0): String {
    return this.enumConstants
        .drop(skipFirst)
        .dropLast(skipLast)
        .map { e -> e.name }
        .joinToString()
}

MyStringEnum.join(1, 1);

我在这里做错了什么?

【问题讨论】:

  • 没有“字符串类型的枚举类”这样的东西。 Enum 是 Enum 类型,T : Enum&lt;T&gt;

标签: enums kotlin kotlin-extension


【解决方案1】:

我建议以下解决方案:

fun <T : Enum<*>> KClass<T>.join(skipFirst: Int = 0, skipLast: Int = 0): String {
    return this.java
            .enumConstants
            .drop(skipFirst)
            .dropLast(skipLast)
            .map { e -> e.name }
            .joinToString()
}

我没有将扩展函数附加到 Class,而是将它附加到 KotlinClass。

现在,您可以简单地使用它了:

enum class Test {ONE, TWO, THREE }

fun main(args: Array<String>) {
    println(Test::class.join())
}
// ONE, TWO, THREE

【讨论】:

  • 好电话,我对 Kotlin 比较陌生,忘记了 KClass
  • 这不是干净的解决方案.. 最好不要编写这样的代码... 谁会知道您应该在 enum 类中寻找函数?你也会忘记...
【解决方案2】:

@IRus 的回答是正确的,但您不必使用反射。对于每个枚举类,编译器会自动生成一个values() 方法。此方法返回一个包含所有条目的数组。我们可以像这样让扩展函数直接对这个数组进行操作:

fun <T : Enum<*>> Array<T>.join(skipFirst: Int = 0, skipLast: Int = 0)
        = drop(skipFirst)
        .dropLast(skipLast)
        .map { e -> e.name }
        .joinToString()

然后这样称呼它:

fun main(args: Array<String>) {
    Test.values().join()
}

【讨论】:

    【解决方案3】:

    使用::class 是一种令人讨厌的解决方法。 我建议您查看 stdlib 中的 enumValues&lt;E&gt;enumValueOf&lt;E&gt; 并执行相同的操作:

    inline fun <reified E : Enum<E>> joinValuesOf(skipFirst: Int = 0, skipLast: Int = 0): String =
            enumValues<E>().join(skipFirst, skipLast)
    
    @PublishedApi
    internal fun Array<out Enum<*>>.join(skipFirst: Int, skipLast: Int): String =
            asList()
                    .subList(skipFirst, size - skipLast)
                    .joinToString(transform = Enum<*>::name)
    

    用法:joinValuesOf&lt;Thread.State&gt;()

    【讨论】:

    • 为什么::class不好?
    • @geg,间接性太多了。 (1) ::class 分配一个特殊的 KClass 对象(当然,它不是免费的),(2) class.java 仅适用于 JVM,并且 (3) Class.enumConstants 使用反射,因此,第一次调用是速度慢,并且可能会因混淆代码而失败。
    【解决方案4】:

    我会用通配符稍微改写你的加入:

    fun <T: Enum<*>> Class<T>.join(skipFirst: Int = 0, skipLast: Int = 0): String {
        return this.enumConstants
                .drop(skipFirst)
                .dropLast(skipLast)
                .map { e -> e.name }
                .joinToString()
    }
    

    然后,假设您的 MyStringEnum 是这样定义的:

    enum class MyStringEnum { FOO, BAR, BAZ }
    

    你可以这样称呼它:

    println(MyStringEnum.values()[0].javaClass.join())
    

    获取输出“FOO, BAR, BAZ”

    由于您在 Class 上定义连接,因此您需要一个实际的 Class 对象来调用它。枚举类显然不是那样工作的,但是它定义的枚举可以产生一个带有javaClass 的类。所以这是我能想到的最好的,我认为符合您要求的一般精神。我不知道是否有更优雅的方式来实现你想要为所有这样的枚举类做的事情。

    编辑:你可以用这个把它收紧一点:

    fun Enum<*>.join(skipFirst: Int = 0, skipLast: Int = 0): String {
        return this.javaClass.join(skipFirst, skipLast)
    }
    

    这让你可以这样调用:

    println(MyStringEnum.values()[0].join())
    

    【讨论】:

    • 啊哈,你让我朝着正确的方向前进。刚试过MyStringEnum::class.java.join(),没有任何抱怨。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-02-04
    • 1970-01-01
    • 2012-07-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多