枚举
枚举用于定义由一组命名值组成的类型。
enum Color:
case Red, Green, Blue
这定义了一个新的sealed
类,Color
,它有三个值,Color.Red
、Color.Green
、Color.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 Planet
或object 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")