枚舉(Enumerations)是一種語言特性,對於建模有限的實體集來講特別有用。一個經典的例子是將工做日建模爲一個枚舉:每一個七天都有一個值。Scala
和許多其餘語言同樣,提供了一種表示枚舉的方法:java
object Weekday extends Enumeration { val Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday = Value }
如今咱們能夠準確清晰地表示工做日,而無需使用String
或Int
等基本類型。Scala
枚舉還提供了一組有用的特性:git
scala> Weekday.Monday.toString res0: String = Monday scala> Weekday.withName("Monday") res1: Weekday.Value = Monday scala> Weekday.withName("Mondai") java.util.NoSuchElementException: No value found for 'Mondai' at scala.Enumeration.withName(Enumeration.scala:124) ... 32 elided
object Weekday extends Enumeration { val Monday = Value("Mo.") val Tuesday = Value("Tu.") val Wednesday = Value("We.") val Thursday = Value("Th.") val Friday = Value("Fr.") val Saturday = Value("Sa.") val Sunday = Value("Su.") } scala> Weekday.Monday.toString res0: String = Mo.
scala> Weekday.values res0: Weekday.ValueSet = Weekday.ValueSet(Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday)
object Weekday extends Enumeration { val Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday = Value } // 按照枚舉值聲明的順序排序 scala> Weekday.values.toList.sorted res0: List[Weekday.Value] = List(Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday)
object Weekday extends Enumeration { val Monday = Value(1) val Tuesday = Value(2) val Wednesday = Value(3) val Thursday = Value(4) val Friday = Value(5) val Saturday = Value(6) val Sunday = Value(0) } // 按照枚舉對應的數字值排序 scala> Weekday.values.toList.sorted res1: List[Weekday.Value] = List(Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday)
然而,這種方法有一些問題。主要有兩個缺點:github
object Weekday extends Enumeration { val Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday = Value } object OtherEnum extends Enumeration { val A, B, C = Value } def test(enum: Weekday.Value) = { println(s"enum: $enum") } def test(enum: OtherEnum.Value) = { println(s"enum: $enum") } <console>:25: error: double definition: def test(enum: Weekday.Value): Unit at line 21 and def test(enum: OtherEnum.Value): Unit at line 25 have same type after erasure: (enum: Enumeration#Value)Unit def test(enum: OtherEnum.Value) = { ^
def nonExhaustive(weekday: Weekday.Value) { weekday match { case Monday => println("I hate Mondays") case Sunday => println("The weekend is already over? :( ") } }
在Scala
中,咱們嚴重依賴於編譯器強大的類型系統,使用這種方法,編譯器不能找到非窮盡模式匹配子句,也不能對不一樣的枚舉使用重載方法。安全
爲了不這種問題,咱們能夠其餘辦法實現枚舉:app
若是您決定使用sealed case objects
,Scala
編譯器能夠解決Scala
枚舉中存在的兩個問題。編譯器既能夠檢測非窮盡模式匹配,也能夠避免類型擦除問題。框架
sealed trait Weekday case object Monday extends Weekday case object Tuesday extends Weekday case object Wednesday extends Weekday case object Thursday extends Weekday case object Friday extends Weekday case object Saturday extends Weekday case object Sunday extends Weekday def test(weekday: Weekday) = { weekday match { case Monday => println("I hate Mondays") case Sunday => println("The weekend is already over? :( ") } } <console>:15: warning: match may not be exhaustive. It would fail on the following inputs: Friday, Saturday, Thursday, Tuesday, Wednesday weekday match { ^ test: (weekday: Weekday)Unit
另外一個很是好的特性是,能夠在枚舉值中包含更多字段(Scala enumerations only provides an index and a name),僅僅使用sealed abstract class
而不是sealed trait
。ide
sealed abstract class Weekday( val name: String, val abbreviation: String, val isWorkDay: Boolean) case object Monday extends Weekday("Monday", "Mo.", true) case object Tuesday extends Weekday("Tuesday", "Tu.", true) case object Wednesday extends Weekday("Wednesday", "We.", true) case object Thursday extends Weekday("Thursday", "Th.", true) case object Friday extends Weekday("Friday", "Fr.", true) case object Saturday extends Weekday("Saturday", "Sa.", false) case object Sunday extends Weekday("Sunday", "Su.", false)
可是這種方式也有它本身的問題:學習
sealed abstract class Weekday( val name: String, val abbreviation: String, val isWeekDay: Boolean, val order: Int) extends Ordered[Weekday] { def compare(that: Weekday) = this.order - that.order } case object Monday extends Weekday("Monday", "Mo.", true, 2) case object Tuesday extends Weekday("Tuesday", "Tu.", true, 3) case object Wednesday extends Weekday("Wednesday", "We.", true, 4) case object Thursday extends Weekday("Thursday", "Th.", true, 5) case object Friday extends Weekday("Friday", "Fr.", true, 6) case object Saturday extends Weekday("Saturday", "Sa.", false, 7) case object Sunday extends Weekday("Sunday", "Su.", false, 1) scala> Monday < Tuesday res0: Boolean = true
itemized是一個OSS lib
,它是rbrick的一部分,rbricks是一種可組合的、佔用空間小的Scala
庫的集合。this
itemized
爲枚舉提供了密封特質層次結構(sealed trait hierarchies)的宏和類型類,回到咱們以前的例子:scala
import io.rbricks.itemized.annotation.enum @enum trait Weekday { object Monday object Tuesday object Wednesday object Thursday object Friday object Saturday object Sunday }
除上面的之外,itemized
還有其餘的一些特性:
scala> import io.rbricks.itemized.ItemizedCodec scala> ItemizedCodec[Weekday].fromRep("Monday") res0: Option[Weekday] = Some(Monday) scala> val weekday: Weekday = Planet.Monday scala> import io.rbricks.itemized.ItemizedCodec.ops._ scala> weekday.toRep res1: String = Earth
儘管itemized
可讓咱們用註解方式建立類型安全的枚舉,可是它也有一些不足:
Enumeratum是一個類型安全且功能強大的Scala
枚舉實現,它提供了詳盡的模式匹配警告。
import enumeratum._ sealed trait Weekday extends EnumEntry object Weekday extends Enum[Weekday] { val values = findValues // mandatory due to Enum extension case object Monday extends Weekday case object Tuesday extends Weekday case object Wednesday extends Weekday case object Thursday extends Weekday case object Friday extends Weekday case object Saturday extends Weekday case object Sunday extends Weekday }
def test(weekday: Weekday) = { weekday match { case Weekday.Monday => println("I hate Mondays") case Weekday.Sunday => println("The weekend is already over? :( ") } } <console>:18: warning: match may not be exhaustive. It would fail on the following inputs: Friday, Saturday, Thursday, Tuesday, Wednesday weekday match { ^ test: (weekday: Weekday)Unit
除了非詳盡的模式匹配警告,enumeratum
還提供:
Enum
繼承上實現)scala> Weekday.withName("Monday") res0: Weekday = Monday scala> Weekday.withName("Momday") java.util.NoSuchElementException: Momday is not a member of Enum (Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday) at enumeratum.Enum$$anonfun$withName$1.apply(Enum.scala:82) at enumeratum.Enum$$anonfun$withName$1.apply(Enum.scala:82) at scala.Option.getOrElse(Option.scala:121) at enumeratum.Enum$class.withName(Enum.scala:81) at Weekday$.withName(<console>:13) ... 43 elided scala> Weekday.withNameOption("Monday") res2: Option[Weekday] = Some(Monday) scala> Weekday.withNameOption("Momday") res3: Option[Weekday] = None
sealed abstract class Weekday( val name: String, val abbreviation: String, val isWorkDay: Boolean) extends EnumEntry case object Weekday extends Enum[Weekday] { val values = findValues case object Monday extends Weekday("Monday", "Mo.", true) case object Tuesday extends Weekday("Tuesday", "Tu.", true) case object Wednesday extends Weekday("Wednesday", "We.", true) case object Thursday extends Weekday("Thursday", "Th.", true) case object Friday extends Weekday("Friday", "Fr.", true) case object Saturday extends Weekday("Saturday", "Sa.", false) case object Sunday extends Weekday("Sunday", "Su.", false) }
sealed abstract class Weekday(val order: Int) extends EnumEntry with Ordered[Weekday] { def compare(that: Weekday) = this.order - that.order } object Weekday extends Enum[Weekday] { val values = findValues case object Monday extends Weekday(2) case object Tuesday extends Weekday(3) case object Wednesday extends Weekday(4) case object Thursday extends Weekday(5) case object Friday extends Weekday(6) case object Saturday extends Weekday(7) case object Sunday extends Weekday(1) }
若是您剛剛開始學習Scala
,我建議使用scala.Enumeration
的方式實現枚舉。當您以爲使用更多Scala
特性更舒服時,以及開始享受編譯器安全性時,能夠試試其餘方式實現枚舉。個人兩個建議是:
sealed hierarchies
enumeratum
,由於它提供了這裏提到的全部特性若是您想看到更多的替代方法,請查看Scala
枚舉的後續內容—Scala Enumerations - Return of the (Java) Jedi