列舉
列舉用於定義由一組命名值組成的類型。
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")