Kotlin:如何使用扩展函数扩展枚举类

21
我试图使用以下函数扩展类型为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);

这里我做错了什么?

1
“enum classes of type String” 这种说法是不存在的。Enum 的类型是 Enum,T : Enum<T> - Miha_x64
4个回答

22
我建议采用以下解决方案:
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()
}

我将扩展函数附加到KotlinClass而不是类上。

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

enum class Test {ONE, TWO, THREE }

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

好的,我对 Kotlin 还比较新,忘记了 KClass。 - geg
这不是一个干净的解决方案...最好不要写这样的代码...谁会知道你应该在枚举类中寻找函数呢?而且你也会忘记... - Renetik

10

使用 ::class 是一种讨厌的解决方法。我建议您查看 stdlib 中的 enumValues<E>enumValueOf<E> 并按照相同的方式处理:

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<Thread.State>()


1
为什么::class不好? - geg
7
@geg,有太多间接性了。(1) ::class 分配了一个特殊的 KClass 对象(当然也不是免费的),(2) class.java 仅适用于 JVM,以及 (3) Class.enumConstants 使用反射,因此第一次调用速度较慢,并且它可能在混淆代码中失败。 - Miha_x64

8
@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()
}

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上的join,因此需要实际的Class对象来调用它。枚举类似乎不起作用,但其定义的枚举可以使用javaClass生成一个Class。因此,我认为这是最符合您请求的一般精神的最佳方案。我不知道是否有更优雅的方法来实现您尝试为所有枚举类做的事情。

编辑:您可以使用以下代码进一步简化:

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() 没有任何问题。 - geg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接