在 Kotlin 枚举类型中出现了循环引用。

14

如何创建一个具有循环引用的枚举类?

以下是一个简单的示例(摘自这个Java问题):

enum class Hand(val beats: Hand) {
    ROCK(SCISSORS), // Enum entry 'SCISSORS' is uninitialized here
    PAPER(ROCK),
    SCISSORS(PAPER);
}
2个回答

16
由于不允许重新分配val属性,因此通常很难解决此问题,并且经常表明数据模型存在问题。有关更广泛上下文的讨论,请参见这个问题/答案
然而,可以使用具有自定义getter(因此没有后备字段)的val属性来解决此简单示例。使用when,getter可以以非常可读的方式定义:
enum class Hand {
    ROCK,
    PAPER,
    SCISSORS;

    val beats: Hand
        get() = when (this) {
            ROCK -> SCISSORS
            PAPER -> ROCK
            SCISSORS -> PAPER
        }
}

另一种解决方案(类似于Eugen Pechanec的答案)是使用密封类。由于其概念较少受限,实现相对于具有重写属性的enum更加简单和易读。1

sealed class Hand {
    abstract val beats: Hand

    object ROCK: Hand() {
        override val beats = SCISSORS
    }

    object PAPER: Hand() {
        override val beats = ROCK
    }

    object SCISSORS: Hand() {
        override val beats = PAPER
    }
}

1个人观点

免责声明:我没有关于这些解决方案如何与经典Java结合使用的信息。


嗯,你使用带有后备字段的属性使代码更易读,这将占用少量内存。我也可以这样做,并且有隐式的 : Hand() ^^ 除此之外,这两种情况编译成几乎相同的字节码。 - Eugen Pechanec

8

没有流程控制语句的替代方案,与mhoff的答案相似:

enum class Hand {
    ROCK {
        override val beats: Hand
            get() = SCISSORS
    },
    PAPER {
        override val beats: Hand
            get() = ROCK
    },
    SCISSORS {
        override val beats: Hand
            get() = PAPER
    };

    abstract val beats: Hand
}

不错的解决方案!我想知道这两种解决方案之间在性能上是否有(显著)差异。 - Michael Hoff
我可以说这肯定会占用更多的内存和磁盘空间。它为ROCK、PAPER和SCISSORS生成一个单例类(扩展Hand)。另一方面,这应该执行得更快 - 你不需要使用switch。 - Eugen Pechanec
我有同样的问题,我使用了这个解决方案,帮助了我很多。 - Caio

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