在 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