Перечисления
Перечисление (an enumeration) может быть использовано для определения типа,
состоящего из конечного набора именованных значений
(в разделе, посвященном моделированию ФП,
будут показаны дополнительные возможности enums
).
Базовые перечисления используются для определения наборов констант, таких как месяцы в году, дни в неделе,
направления, такие как север/юг/восток/запад, и многое другое.
В качестве примера, рассмотрим перечисления, определяющие наборы атрибутов, связанных с пиццами:
enum CrustSize:
case Small, Medium, Large
enum CrustType:
case Thin, Thick, Regular
enum Topping:
case Cheese, Pepperoni, BlackOlives, GreenOlives, Onions
Для использования в коде enum
необходимо импортировать:
import CrustSize.*
val currentCrustSize = Small
Значения enum
-ов можно сравнивать и использовать в матчинге:
if (currentCrustSize == Small)
println("If you buy a large pizza, you'll get a prize!")
// If you buy a large pizza, you'll get a prize!
currentCrustSize match
case Small => println("small")
case Medium => println("medium")
case Large => println("large")
// small
Параметризованные перечисления
Перечисления могут иметь параметры конструктора:
enum Color(val rgb: Int):
case Red extends Color(0xFF0000)
case Green extends Color(0x00FF00)
case Blue extends Color(0x0000FF)
Методы, определенные для enum
Значения enum соответствуют уникальным целым числам.
Целое число, связанное со значением перечисления, возвращается методом ordinal
:
val red = Color.Red
// red: Color = Red
red.ordinal
// res3: Int = 0
Сопутствующий объект перечисления определяет также три служебных метода.
Метод valueOf
получает значение enum по его имени.
Метод values
возвращает все значения enum, определенные в перечислении, в виде Array
.
Метод fromOrdinal
получает значение перечисления по его порядковому (Int
) значению.
Color.valueOf("Blue")
Color.values
Color.fromOrdinal(0)
Перечисления могут содержать параметры и методы:
В перечисление можно добавить свои собственные определения. Пример:
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
Ограничения для enum case
Объявления case-enum аналогичны вторичным конструкторам: их область действия находится за пределами шаблона enum, несмотря на то, что они объявлены внутри него. Это означает, что объявления case enum не могут получить доступ к внутренним членам класса enum.
Точно так же объявления case enum не могут напрямую ссылаться на члены сопутствующего объекта перечисления, даже если они импортированы (напрямую или путем переименования). Например:
import Planet.*
enum Planet(mass: Double, radius: Double):
private final val (mercuryMass, mercuryRadius) = (3.303e+23, 2.4397e6)
case Mercury extends Planet(mercuryMass, mercuryRadius) // нет доступа
case Venus extends Planet(venusMass, venusRadius) // невалидная ссылка
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
.
Совместимость с Java enums
Если необходимо использовать определенные в Scala перечисления в качестве перечислений Java,
можно сделать это, расширив класс java.lang.Enum
(который импортируется по умолчанию) следующим образом:
enum Color extends Enum[Color] { case Red, Green, Blue }
Параметр типа берется из определения Java enum
и должен совпадать с типом перечисления.
Нет необходимости предоставлять аргументы конструктора (как определено в документах Java API) для java.lang.Enum
при его расширении — компилятор генерирует их автоматически.
После определения Color
его можно использовать так же, как если бы использовался Java enum
:
Color.Red.compareTo(Color.Green)
Ссылки: