Scala中的Case对象与枚举

在Scala中,何时使用case类(或case对象)与扩展枚举有什么最佳实践指南吗?

它们似乎提供了一些相同的好处。

125756 次浏览

与枚举相比,使用case类的优点是:

  • 当使用密封大小写类时,Scala编译器可以判断匹配是否完全指定,例如,当所有可能的匹配都支持在匹配声明中。对于枚举,Scala编译器无法判断。
  • Case类自然比支持名称和ID的基于值的枚举支持更多的字段。

使用枚举而不是case类的优点是:

  • 枚举通常需要编写更少的代码。
  • 对于Scala新手来说,枚举比较容易理解,因为它们在其他语言中很普遍

所以一般来说,如果你只需要一个简单的常量列表的名称,使用枚举。否则,如果你需要一些更复杂的东西,或者想要编译器告诉你是否指定了所有匹配的额外安全,用例类。

一个很大的区别是,__abc0支持从一些name String实例化它们。例如:

object Currency extends Enumeration {
val GBP = Value("GBP")
val EUR = Value("EUR") //etc.
}

然后你可以这样做:

val ccy = Currency.withName("EUR")

这在希望持久化枚举(例如,到数据库)或从驻留在文件中的数据创建枚举时非常有用。然而,我发现一般来说,Scala中的枚举有点笨拙,有一种笨拙的附加组件的感觉,所以我现在倾向于使用case objects。case object比enum更灵活:

sealed trait Currency { def name: String }
case object EUR extends Currency { val name = "EUR" } //etc.


case class UnknownCurrency(name: String) extends Currency

所以现在我的优势是……

trade.ccy match {
case EUR                   =>
case UnknownCurrency(code) =>
}

正如@chaotic3quilibrium指出的那样(为了便于阅读,做了一些更正):

关于“UnknownCurrency(代码)“;模式,有其他方法来处理找不到货币代码字符串,而不是“break”;Currency类型的闭集性质。类型为CurrencyUnknownCurrency现在可以潜入API的其他部分。

建议将这种情况推到Enumeration之外,并让客户端处理一个Option[Currency]类型,该类型将清楚地表明确实存在匹配问题,并且“鼓励”。API的用户自己来整理。

为了跟进这里的其他答案,__abc0相对于__abc1的主要缺点是:

  1. < p > 不能遍历“枚举”的所有实例;。这当然是事实,但我发现在实践中很少需要这样做。

  2. < p > 不能很容易地从持久化值实例化。这也是正确的,但是,除了在大量枚举的情况下(例如,所有货币),这并不会带来巨大的开销。

当您需要遍历或过滤所有实例时,case类相对于枚举的另一个缺点。这是枚举(以及Java枚举)的内置功能,而case类并不自动支持这种功能。

换句话说:“没有简单的方法可以获得包含case类的全部枚举值集的列表”。

我见过让case类模拟枚举的各种版本。以下是我的看法:

trait CaseEnumValue {
def name:String
}


trait CaseEnum {
type V <: CaseEnumValue
def values:List[V]
def unapply(name:String):Option[String] = {
if (values.exists(_.name == name)) Some(name) else None
}
def unapply(value:V):String = {
return value.name
}
def apply(name:String):Option[V] = {
values.find(_.name == name)
}
}

它允许你构造如下所示的case类:

abstract class Currency(override name:String) extends CaseEnumValue {
}


object Currency extends CaseEnum {
type V = Site
case object EUR extends Currency("EUR")
case object GBP extends Currency("GBP")
var values = List(EUR, GBP)
}

也许有人可以想出一个更好的技巧,而不是像我这样简单地向列表中添加一个each case类。这是我当时所能想到的。

Case对象已经为它们的toString方法返回它们的名称,因此单独传入它是不必要的。下面是一个类似于jho的版本(为简洁起见,省略了方便方法):

trait Enum[A] {
trait Value { self: A => }
val values: List[A]
}


sealed trait Currency extends Currency.Value
object Currency extends Enum[Currency] {
case object EUR extends Currency
case object GBP extends Currency
val values = List(EUR, GBP)
}

对象是懒惰的;通过使用vals,我们可以删除列表,但必须重复名称:

trait Enum[A <: {def name: String}] {
trait Value { self: A =>
_values :+= this
}
private var _values = List.empty[A]
def values = _values
}


sealed abstract class Currency(val name: String) extends Currency.Value
object Currency extends Enum[Currency] {
val EUR = new Currency("EUR") {}
val GBP = new Currency("GBP") {}
}

如果不介意作弊,可以使用反射API或谷歌Reflections之类的东西预加载枚举值。非惰性大小写对象提供了最简洁的语法:

trait Enum[A] {
trait Value { self: A =>
_values :+= this
}
private var _values = List.empty[A]
def values = _values
}


sealed trait Currency extends Currency.Value
object Currency extends Enum[Currency] {
case object EUR extends Currency
case object GBP extends Currency
}

漂亮而干净,具有case类和Java枚举的所有优点。就我个人而言,我在对象之外定义枚举值,以更好地匹配惯用的Scala代码:

object Currency extends Enum[Currency]
sealed trait Currency extends Currency.Value
case object EUR extends Currency
case object GBP extends Currency

更新:下面的代码有一个错误,描述为在这里。下面的测试程序可以工作,但是如果您使用DayOfWeek。Mon(例如)在DayOfWeek本身之前,它会失败,因为DayOfWeek还没有初始化(使用内部对象不会导致外部对象被初始化)。如果你在主类中执行val enums = Seq( DayOfWeek )之类的操作,强制初始化枚举,或者你可以使用chaotic3equilibrium的修改,你仍然可以使用这段代码。期待一个基于宏的枚举!


如果你愿意

  • 关于非穷尽模式匹配的警告
  • 分配给每个枚举值的Int ID,您可以选择控制它
  • 一个不可变的枚举值列表,按照它们定义的顺序
  • 从名称到enum值的不可变映射
  • 从id到enum值的不可变映射
  • 为所有或特定枚举值粘贴方法/数据,或为整个枚举粘贴方法/数据的位置
  • 排序enum值(这样您就可以测试,例如,day <星期三)
  • 扩展一个枚举以创建其他枚举的能力

那么下面的内容可能会让你感兴趣。欢迎您的反馈。

在这个实现中,有抽象的Enum和EnumVal基类,可以对它们进行扩展。我们将在一分钟内看到这些类,但首先,这是你如何定义一个枚举:

object DayOfWeek extends Enum {
sealed abstract class Val extends EnumVal
case object Mon extends Val; Mon()
case object Tue extends Val; Tue()
case object Wed extends Val; Wed()
case object Thu extends Val; Thu()
case object Fri extends Val; Fri()
case object Sat extends Val; Sat()
case object Sun extends Val; Sun()
}

请注意,您必须使用每个枚举值(调用其apply方法)才能使其具有生命。[我希望内部对象不懒惰,除非我特别要求它们懒惰。我认为。)

当然,我们可以向DayOfWeek、Val或个别案例对象添加方法/数据,如果我们愿意的话。

下面是如何使用这样的枚举:

object DayOfWeekTest extends App {


// To get a map from Int id to enum:
println( DayOfWeek.valuesById )


// To get a map from String name to enum:
println( DayOfWeek.valuesByName )


// To iterate through a list of the enum values in definition order,
// which can be made different from ID order, and get their IDs and names:
DayOfWeek.values foreach { v => println( v.id + " = " + v ) }


// To sort by ID or name:
println( DayOfWeek.values.sorted mkString ", " )
println( DayOfWeek.values.sortBy(_.toString) mkString ", " )


// To look up enum values by name:
println( DayOfWeek("Tue") ) // Some[DayOfWeek.Val]
println( DayOfWeek("Xyz") ) // None


// To look up enum values by id:
println( DayOfWeek(3) )         // Some[DayOfWeek.Val]
println( DayOfWeek(9) )         // None


import DayOfWeek._


// To compare enums as ordinals:
println( Tue < Fri )


// Warnings about non-exhaustive pattern matches:
def aufDeutsch( day: DayOfWeek.Val ) = day match {
case Mon => "Montag"
case Tue => "Dienstag"
case Wed => "Mittwoch"
case Thu => "Donnerstag"
case Fri => "Freitag"
// Commenting these out causes compiler warning: "match is not exhaustive!"
// case Sat => "Samstag"
// case Sun => "Sonntag"
}


}

下面是你编译它时得到的结果:

DayOfWeekTest.scala:31: warning: match is not exhaustive!
missing combination            Sat
missing combination            Sun


def aufDeutsch( day: DayOfWeek.Val ) = day match {
^
one warning found

您可以将“day match”替换为“(day: @unchecked) match”,因为您不想要这样的警告,或者简单地在结尾包含一个笼统的情况。

当你运行上面的程序时,你会得到这样的输出:

Map(0 -> Mon, 5 -> Sat, 1 -> Tue, 6 -> Sun, 2 -> Wed, 3 -> Thu, 4 -> Fri)
Map(Thu -> Thu, Sat -> Sat, Tue -> Tue, Sun -> Sun, Mon -> Mon, Wed -> Wed, Fri -> Fri)
0 = Mon
1 = Tue
2 = Wed
3 = Thu
4 = Fri
5 = Sat
6 = Sun
Mon, Tue, Wed, Thu, Fri, Sat, Sun
Fri, Mon, Sat, Sun, Thu, Tue, Wed
Some(Tue)
None
Some(Thu)
None
true

注意,由于List和Maps是不可变的,所以可以轻松地删除元素以创建子集,而不会破坏枚举本身。

下面是Enum类本身(以及其中的EnumVal):

abstract class Enum {


type Val <: EnumVal


protected var nextId: Int = 0


private var values_       =       List[Val]()
private var valuesById_   = Map[Int   ,Val]()
private var valuesByName_ = Map[String,Val]()


def values       = values_
def valuesById   = valuesById_
def valuesByName = valuesByName_


def apply( id  : Int    ) = valuesById  .get(id  )  // Some|None
def apply( name: String ) = valuesByName.get(name)  // Some|None


// Base class for enum values; it registers the value with the Enum.
protected abstract class EnumVal extends Ordered[Val] {
val theVal = this.asInstanceOf[Val]  // only extend EnumVal to Val
val id = nextId
def bumpId { nextId += 1 }
def compare( that:Val ) = this.id - that.id
def apply() {
if ( valuesById_.get(id) != None )
throw new Exception( "cannot init " + this + " enum value twice" )
bumpId
values_ ++= List(theVal)
valuesById_   += ( id       -> theVal )
valuesByName_ += ( toString -> theVal )
}
}


}

下面是它的一个更高级的使用,它控制id,并将数据/方法添加到Val抽象和枚举本身:

object DayOfWeek extends Enum {


sealed abstract class Val( val isWeekday:Boolean = true ) extends EnumVal {
def isWeekend = !isWeekday
val abbrev = toString take 3
}
case object    Monday extends Val;    Monday()
case object   Tuesday extends Val;   Tuesday()
case object Wednesday extends Val; Wednesday()
case object  Thursday extends Val;  Thursday()
case object    Friday extends Val;    Friday()
nextId = -2
case object  Saturday extends Val(false); Saturday()
case object    Sunday extends Val(false);   Sunday()


val (weekDays,weekendDays) = values partition (_.isWeekday)
}

在过去几次我需要这两个选择的时候,我一直在反复考虑。直到最近,我的偏好一直是密封的trait/case对象选项。

1) Scala枚举声明

object OutboundMarketMakerEntryPointType extends Enumeration {
type OutboundMarketMakerEntryPointType = Value


val Alpha, Beta = Value
}

2)封印属性+个案对象

sealed trait OutboundMarketMakerEntryPointType


case object AlphaEntryPoint extends OutboundMarketMakerEntryPointType


case object BetaEntryPoint extends OutboundMarketMakerEntryPointType

虽然这两种方法都不能满足java枚举提供的所有功能,但以下是优点和缺点:

Scala枚举

< p >优点: -使用选项实例化或直接假设准确的函数(从持久存储加载时更容易) -支持迭代所有可能的值

< p >缺点: -不支持非穷举搜索的编译警告(使模式匹配不太理想)

Case对象/密封特征

< p >优点: 使用密封特征,我们可以预实例化一些值,而其他值可以在创建时注入 -完全支持模式匹配(应用/取消应用定义的方法)

< p >缺点: -从持久存储实例化-你通常必须在这里使用模式匹配或定义你自己的所有可能的“enum值”列表

最终让我改变观点的是以下片段:

object DbInstrumentQueries {
def instrumentExtractor(tableAlias: String = "s")(rs: ResultSet): Instrument = {
val symbol = rs.getString(tableAlias + ".name")
val quoteCurrency = rs.getString(tableAlias + ".quote_currency")
val fixRepresentation = rs.getString(tableAlias + ".fix_representation")
val pointsValue = rs.getInt(tableAlias + ".points_value")
val instrumentType = InstrumentType.fromString(rs.getString(tableAlias +".instrument_type"))
val productType = ProductType.fromString(rs.getString(tableAlias + ".product_type"))


Instrument(symbol, fixRepresentation, quoteCurrency, pointsValue, instrumentType, productType)
}
}


object InstrumentType {
def fromString(instrumentType: String): InstrumentType = Seq(CurrencyPair, Metal, CFD)
.find(_.toString == instrumentType).get
}


object ProductType {


def fromString(productType: String): ProductType = Seq(Commodity, Currency, Index)
.find(_.toString == productType).get
}

.get调用是可怕的-使用枚举代替,我可以简单地调用枚举上的withName方法,如下所示:

object DbInstrumentQueries {
def instrumentExtractor(tableAlias: String = "s")(rs: ResultSet): Instrument = {
val symbol = rs.getString(tableAlias + ".name")
val quoteCurrency = rs.getString(tableAlias + ".quote_currency")
val fixRepresentation = rs.getString(tableAlias + ".fix_representation")
val pointsValue = rs.getInt(tableAlias + ".points_value")
val instrumentType = InstrumentType.withNameString(rs.getString(tableAlias + ".instrument_type"))
val productType = ProductType.withName(rs.getString(tableAlias + ".product_type"))


Instrument(symbol, fixRepresentation, quoteCurrency, pointsValue, instrumentType, productType)
}
}

所以我认为我的首选是使用枚举当值打算从存储库访问和case对象/密封特征。

如果你想要维护与其他JVM语言(如Java)的互操作性,那么最好的选择是编写Java枚举。它们在Scala和Java代码中都是透明工作的,这比scala.Enumeration或case对象要好得多。如果可以避免的话,让我们不要为GitHub上的每个新爱好项目都创建一个新的枚举库!

< >强更新: 一个新的宏观解决方案已经创建,它远远优于我下面概述的解决方案。我强烈推荐使用这个新的宏观解决方案Dotty的计划似乎将使这种枚举解决方案成为语言的一部分。 Whoohoo !< / p > < p > 简介: < br > 在Scala项目中尝试重现Java Enum有三种基本模式。三种模式中的两种;直接使用Java Enumscala.Enumeration,不能启用Scala的穷举模式匹配。第三个;“密封特质+格对象”,是否…但有JVM类/对象初始化的复杂性导致不一致的序号索引生成。< / p >

我已经创建了一个解决方案与两个类;枚举EnumerationDecorated,位于这个要点。我没有将代码发布到这个线程中,因为枚举的文件相当大(+400行-包含大量解释实现上下文的注释)。

细节: < br > 你问的问题很一般;"...什么时候使用caseobjects vs扩展[scala.]Enumeration”。事实证明,有许多可能的答案,每个答案都取决于您所拥有的特定项目需求的微妙之处。答案可以归结为三种基本模式。< / p >

首先,让我们确保使用的是与枚举相同的基本概念。让我们主要根据Enum由Java 5(1.5提供)定义一个枚举:

  1. 包含命名成员的自然有序闭集
    1. 有固定数量的成员
    2. 成员自然排序并显式索引
      • 而不是根据一些inate成员可查询条件进行排序
      • 李< / ul > < / >
      • 在所有成员的总集中,每个成员都有唯一的名称
      • 李< / ol > < / >
      • 可以根据索引轻松遍历所有成员
      • 成员可以通过其名称(区分大小写)进行检索
        1. 如果可以使用不区分大小写的名称检索成员,那就太好了
        2. 李< / ol > < / >
        3. 可以通过成员的索引检索成员
        4. 成员可以轻松、透明和有效地使用序列化
        5. 可以很容易地扩展成员以保存额外的关联单例数据
        6. 除了Java的Enum之外,如果能够显式地利用Scala对枚举的模式匹配穷尽性检查就更好了
        接下来,让我们看看三种最常见的解决方案模式的简化版本:

        一)实际上直接使用Java Enum模式(在混合Scala/Java项目中):

        public enum ChessPiece {
        KING('K', 0)
        , QUEEN('Q', 9)
        , BISHOP('B', 3)
        , KNIGHT('N', 3)
        , ROOK('R', 5)
        , PAWN('P', 1)
        ;
        
        
        private char character;
        private int pointValue;
        
        
        private ChessPiece(char character, int pointValue) {
        this.character = character;
        this.pointValue = pointValue;
        }
        
        
        public int getCharacter() {
        return character;
        }
        
        
        public int getPointValue() {
        return pointValue;
        }
        }
        

        枚举定义中的以下项不可用:

        1. 如果一个成员也可以用它不区分大小写的名字来检索,那就太好了
        2. 7 -超越Java的Enum,如果能够显式地利用Scala的模式匹配耗尽性检查枚举就好了
        对于我目前的项目,我没有在Scala/Java混合项目路径上冒险的好处。即使我可以选择做一个混合项目,如果/当我添加/删除枚举成员,或者正在编写一些新代码来处理现有的枚举成员,第7项对于允许我捕获编译时问题是至关重要的

        使用"__abc0 + __abc1"模式:

        sealed trait ChessPiece {def character: Char; def pointValue: Int}
        object ChessPiece {
        case object KING extends ChessPiece {val character = 'K'; val pointValue = 0}
        case object QUEEN extends ChessPiece {val character = 'Q'; val pointValue = 9}
        case object BISHOP extends ChessPiece {val character = 'B'; val pointValue = 3}
        case object KNIGHT extends ChessPiece {val character = 'N'; val pointValue = 3}
        case object ROOK extends ChessPiece {val character = 'R'; val pointValue = 5}
        case object PAWN extends ChessPiece {val character = 'P'; val pointValue = 1}
        }
        

        枚举定义中的以下项不可用:

        1. 1.2 -成员自然有序并显式建立索引
        2. 2 -所有成员都可以根据它们的索引进行迭代
        3. 成员可以通过其名称(区分大小写)进行检索
        4. 如果一个成员也可以用它不区分大小写的名字来检索,那就太好了
        5. 成员可以用它的索引来检索

        这是有争议的,它确实符合枚举定义第5和6项。对于5人来说,说它是有效的有点牵强。对于6,扩展以保存额外的关联单例数据并不容易

        使用scala.Enumeration模式(受到这个StackOverflow的答案的启发):

        object ChessPiece extends Enumeration {
        val KING = ChessPieceVal('K', 0)
        val QUEEN = ChessPieceVal('Q', 9)
        val BISHOP = ChessPieceVal('B', 3)
        val KNIGHT = ChessPieceVal('N', 3)
        val ROOK = ChessPieceVal('R', 5)
        val PAWN = ChessPieceVal('P', 1)
        protected case class ChessPieceVal(character: Char, pointValue: Int) extends super.Val()
        implicit def convert(value: Value) = value.asInstanceOf[ChessPieceVal]
        }
        

        枚举定义中的以下项不可用(恰好与直接使用Java Enum的列表相同):

        1. 如果一个成员也可以用它不区分大小写的名字来检索,那就太好了
        2. 7 -超越Java的Enum,如果能够显式地利用Scala的模式匹配耗尽性检查枚举就好了

        同样,对于我当前的项目,第7项对于允许我在添加/删除枚举成员或编写一些新代码来处理现有枚举成员时捕获编译时问题至关重要。


        因此,给定上面的枚举定义,以上三个解决方案都不能工作,因为它们不能提供上面枚举定义中概述的所有内容:

        1. Java Enum直接在混合Scala/Java项目
        2. “密封trait + case对象”
        3. scala。枚举

        这些解决方案中的每一个最终都可以被重做/扩展/重构,以试图覆盖每个解决方案中缺失的一些需求。然而,Java Enumscala.Enumeration解决方案都不能充分扩展以提供第7项。在我自己的项目中,这是在Scala中使用封闭类型的更有吸引力的价值之一。我强烈倾向于通过编译时警告/错误来表明代码中存在漏洞/问题,而不是必须从生产运行时异常/失败中收集漏洞/问题。


        在这方面,我开始使用case object路径,看看是否可以产生一个涵盖上述所有枚举定义的解决方案。第一个挑战是通过JVM类/对象初始化的核心问题(在这个StackOverflow帖子中详细介绍)。我终于找到了解决办法。

        我的解决方案有两个特点;枚举EnumerationDecorated,由于Enumeration特征超过+400行长(很多注释解释上下文),我放弃将它粘贴到这个线程(这将使它延伸到相当大的页面)。有关详细信息,请直接跳转到要点

        下面是使用与上面相同的数据思想(完整注释版本可以在这里)并在EnumerationDecorated中实现的解决方案。

        import scala.reflect.runtime.universe.{TypeTag,typeTag}
        import org.public_domain.scala.utils.EnumerationDecorated
        
        
        object ChessPiecesEnhancedDecorated extends EnumerationDecorated {
        case object KING extends Member
        case object QUEEN extends Member
        case object BISHOP extends Member
        case object KNIGHT extends Member
        case object ROOK extends Member
        case object PAWN extends Member
        
        
        val decorationOrderedSet: List[Decoration] =
        List(
        Decoration(KING,   'K', 0)
        , Decoration(QUEEN,  'Q', 9)
        , Decoration(BISHOP, 'B', 3)
        , Decoration(KNIGHT, 'N', 3)
        , Decoration(ROOK,   'R', 5)
        , Decoration(PAWN,   'P', 1)
        )
        
        
        final case class Decoration private[ChessPiecesEnhancedDecorated] (member: Member, char: Char, pointValue: Int) extends DecorationBase {
        val description: String = member.name.toLowerCase.capitalize
        }
        override def typeTagMember: TypeTag[_] = typeTag[Member]
        sealed trait Member extends MemberDecorated
        }
        

        这是我创建的一对新枚举特征(位于这个要点中)的示例使用,以实现枚举定义中所要求和概述的所有功能。

        一个需要注意的问题是枚举成员名必须重复(上面例子中的decorationOrderedSet)。虽然我确实把它减少到一次重复,但由于两个问题,我不知道如何让它更少:

        1. 这个特定对象/case对象模型的JVM对象/类初始化是未定义的(参见这个Stackoverflow线程)
        2. 方法getClass.getDeclaredClasses返回的内容有一个未定义的顺序(它不太可能与源代码中case object声明的顺序相同)

        考虑到这两个问题,我不得不放弃尝试生成隐含的排序,而必须显式地要求客户端用某种有序集概念定义和声明它。由于Scala集合没有插入有序集实现,我能做的最好的是使用List,然后运行时检查它是否真的是一个集合。这不是我想要的实现方式。

        考虑到设计需要第二个列表/集排序val,考虑到上面的ChessPiecesEnhancedDecorated示例,有可能添加case object PAWN2 extends Member,然后忘记将Decoration(PAWN2,'P2', 2)添加到decorationOrderedSet。因此,有一个运行时检查来验证列表不仅是一个集合,而且包含扩展sealed trait Member的所有case对象。这是一种特殊形式的反射/宏地狱

        请在要点上留下评论和/或反馈

我在这里有一个简单的库,允许你使用密封的trait /类作为枚举值,而不必维护自己的值列表。它依赖于一个简单的宏,不依赖于有bug的knownDirectSubclasses

https://github.com/lloydmeta/enumeratum < a href = " https://github.com/lloydmeta/enumeratum " > < / >

我更喜欢case objects(这是个人喜好的问题)。为了解决这种方法固有的问题(解析字符串并遍历所有元素),我添加了一些不完美但有效的行。

我把代码粘贴在这里,希望它有用,也希望其他人可以改进它。

/**
* Enum for Genre. It contains the type, objects, elements set and parse method.
*
* This approach supports:
*
* - Pattern matching
* - Parse from name
* - Get all elements
*/
object Genre {
sealed trait Genre


case object MALE extends Genre
case object FEMALE extends Genre


val elements = Set (MALE, FEMALE) // You have to take care this set matches all objects


def apply (code: String) =
if (MALE.toString == code) MALE
else if (FEMALE.toString == code) FEMALE
else throw new IllegalArgumentException
}


/**
* Enum usage (and tests).
*/
object GenreTest extends App {
import Genre._


val m1 = MALE
val m2 = Genre ("MALE")


assert (m1 == m2)
assert (m1.toString == "MALE")


val f1 = FEMALE
val f2 = Genre ("FEMALE")


assert (f1 == f2)
assert (f1.toString == "FEMALE")


try {
Genre (null)
assert (false)
}
catch {
case e: IllegalArgumentException => assert (true)
}


try {
Genre ("male")
assert (false)
}
catch {
case e: IllegalArgumentException => assert (true)
}


Genre.elements.foreach { println }
}
对于那些仍然在寻找如何获得盖茨是工作的答案的人: 你可以在声明case对象后引用它来实例化它:

trait Enum[A] {
trait Value { self: A =>
_values :+= this
}
private var _values = List.empty[A]
def values = _values
}


sealed trait Currency extends Currency.Value
object Currency extends Enum[Currency] {
case object EUR extends Currency;
EUR //THIS IS ONLY CHANGE
case object GBP extends Currency; GBP //Inline looks better
}

2017年3月更新:根据安东尼飞来的评论,scala.Enumeration/enum PR已经关闭。

< a href = " http://dotty.epfl。ch / noreferrer“rel = >多点的< / > (Scala的下一代编译器)将占据主导地位,尽管1970年的多蒂号马丁·奥德斯基1958年的公关


注:现在(2016年8月,6年多后)有一个建议删除scala.Enumeration: 5352年公关

弃用scala.Enumeration,添加@enum注释

的语法

@enum
class Toggle {
ON
OFF
}

是一个可能的实现示例,目的是也支持符合某些限制(没有嵌套,递归或变化的构造函数参数)的adt,例如:

@enum
sealed trait Toggle
case object ON  extends Toggle
case object OFF extends Toggle

弃用未减轻的灾难scala.Enumeration

@enum相对于scala的优势。枚举:

  • 实际工作
  • Java互操作
  • 无擦除问题
  • 定义枚举时不需要学习令人困惑的迷你dsl

缺点:没有。

这解决了不能有一个代码库的问题 支持Scala- jvm, Scala.js和Scala- native (Scala.js/Scala-Native不支持Java源代码,Scala源代码不能在Scala- jvm上定义被现有api接受的枚举)

我认为使用case classes相对于enumerations的最大优势是你可以使用类型的类模式,也就是特别polymorphysm。不需要像这样匹配枚举:

someEnum match {
ENUMA => makeThis()
ENUMB => makeThat()
}

相反,你会看到如下内容:

def someCode[SomeCaseClass](implicit val maker: Maker[SomeCaseClass]){
maker.make()
}


implicit val makerA = new Maker[CaseClassA]{
def make() = ...
}
implicit val makerB = new Maker[CaseClassB]{
def make() = ...
}