在 GitHub 上编辑此页面

枚举

枚举用于定义由一组命名值组成的类型。

enum Color:
  case Red, Green, Blue

这定义了一个新的sealed类,Color,它有三个值,Color.RedColor.GreenColor.Blue。颜色值是Color伴生对象的成员。

参数化枚举

枚举可以参数化。

enum Color(val rgb: Int):
  case Red   extends Color(0xFF0000)
  case Green extends Color(0x00FF00)
  case Blue  extends Color(0x0000FF)

如示例所示,你可以使用显式 extends 子句来定义参数值。

为枚举定义的方法

枚举的值对应于唯一的整数。与枚举值关联的整数由其ordinal方法返回

scala> val red = Color.Red
val red: Color = Red
scala> red.ordinal
val res0: Int = 0

枚举的伴生对象还定义了三个实用方法。valueOf方法通过名称获取枚举值。values方法以Array的形式返回枚举中定义的所有枚举值。fromOrdinal方法从其序数(Int)值获取枚举值。

scala> Color.valueOf("Blue")
val res0: Color = Blue
scala> Color.values
val res1: Array[Color] = Array(Red, Green, Blue)
scala> Color.fromOrdinal(0)
val res2: Color = Red

枚举的用户定义成员

可以向枚举添加你自己的定义。示例

enum Planet(mass: Double, radius: Double):
  private final val G = 6.67300E-11
  def surfaceGravity = G * mass / (radius * radius)
  def surfaceWeight(otherMass: Double) = otherMass * surfaceGravity

  case Mercury extends Planet(3.303e+23, 2.4397e6)
  case Venus   extends Planet(4.869e+24, 6.0518e6)
  case Earth   extends Planet(5.976e+24, 6.37814e6)
  case Mars    extends Planet(6.421e+23, 3.3972e6)
  case Jupiter extends Planet(1.9e+27,   7.1492e7)
  case Saturn  extends Planet(5.688e+26, 6.0268e7)
  case Uranus  extends Planet(8.686e+25, 2.5559e7)
  case Neptune extends Planet(1.024e+26, 2.4746e7)
end Planet

用户定义的枚举伴生对象

还可以为枚举定义一个显式的伴生对象

object Planet:
  def main(args: Array[String]) =
    val earthWeight = args(0).toDouble
    val mass = earthWeight / Earth.surfaceGravity
    for p <- values do
      println(s"Your weight on $p is ${p.surfaceWeight(mass)}")
end Planet

枚举案例的限制

枚举案例声明类似于辅助构造函数:它们的作用域在枚举模板之外,尽管在其中声明。这意味着枚举案例声明不能访问枚举类的内部成员。

类似地,枚举案例声明可能不会直接引用枚举伴生对象中的成员,即使它们被导入(直接或通过重命名)。例如

import Planet.*
enum Planet(mass: Double, radius: Double):
  private final val (mercuryMass, mercuryRadius) = (3.303e+23, 2.4397e6)

  case Mercury extends Planet(mercuryMass, mercuryRadius)             // Not found
  case Venus   extends Planet(venusMass, venusRadius)                 // illegal reference
  case Earth   extends Planet(Planet.earthMass, Planet.earthRadius)   // ok
object Planet:
  private final val (venusMass, venusRadius) = (4.869e+24, 6.0518e6)
  private final val (earthMass, earthRadius) = (5.976e+24, 6.37814e6)
end Planet

Mercury引用的字段不可见,而Venus引用的字段不能直接引用(使用import Planet.*)。您必须使用间接引用,例如使用Earth演示的那样。

枚举案例的弃用

作为库作者,您可能希望发出信号,表明枚举案例不再打算使用。但是,您仍然希望优雅地处理从公共 API 中删除案例,例如对弃用案例进行特殊处理。

为了说明,假设Planet枚举最初有一个附加案例

 enum Planet(mass: Double, radius: Double):
   ...
   case Neptune extends Planet(1.024e+26, 2.4746e7)
+  case Pluto   extends Planet(1.309e+22, 1.1883e3)
 end Planet

我们现在要弃用Pluto案例。首先,我们将scala.deprecated注释添加到Pluto

 enum Planet(mass: Double, radius: Double):
   ...
   case Neptune extends Planet(1.024e+26, 2.4746e7)
-  case Pluto   extends Planet(1.309e+22, 1.1883e3)
+
+  @deprecated("refer to IAU definition of planet")
+  case Pluto extends Planet(1.309e+22, 1.1883e3)
 end Planet

enum Planetobject Planet的词法作用域之外,对Planet.Pluto的引用将产生弃用警告,但在这些作用域内,我们仍然可以引用它来实现对弃用案例的内省

trait Deprecations[T <: reflect.Enum] {
  extension (t: T) def isDeprecatedCase: Boolean
}

object Planet {
  given Deprecations[Planet] with {
    extension (p: Planet)
      def isDeprecatedCase = p == Pluto
  }
}

我们可以想象,一个库可以利用类型类派生自动为Deprecations提供一个实例。

与 Java 枚举的兼容性

如果您想将 Scala 定义的枚举用作Java 枚举,可以通过扩展类java.lang.Enum来实现,该类默认导入,如下所示

enum Color extends Enum[Color] { case Red, Green, Blue }

类型参数来自 Java 枚举定义,应与枚举的类型相同。在扩展java.lang.Enum时,无需向其提供构造函数参数(如 Java API 文档中定义的)——编译器会自动生成它们。

在像这样定义 Color 之后,您可以像使用 Java 枚举一样使用它

scala> Color.Red.compareTo(Color.Green)
val res15: Int = -1

有关从 Java 使用 Scala 3 枚举的更深入示例,请参阅 此测试。在测试中,枚举在 MainScala.scala 文件中定义,并从 Java 源 Test.java 使用。

实现

枚举表示为扩展 scala.reflect.Enum 特征的 sealed 类。此特征定义了一个公共方法 ordinal

package scala.reflect

/** A base trait of all Scala enum definitions */
transparent trait Enum extends Any, Product, Serializable:

  /** A number uniquely identifying a case of an enum */
  def ordinal: Int

带有 extends 子句的枚举值扩展为匿名类实例。例如,上面的 Venus 值将这样定义

val Venus: Planet = new Planet(4.869E24, 6051800.0):
  def ordinal: Int = 1
  override def productPrefix: String = "Venus"
  override def toString: String = "Venus"

没有 extends 子句的枚举值都共享一个单一实现,可以使用一个将标记和名称作为参数的私有方法来实例化它。例如,上面值 Color.Red 的第一个定义将扩展为

val Red: Color = $new(0, "Red")

参考

有关更多信息,请参阅 问题 #1970PR #4003