I was wondering if there are any best practice guidelines on when to use case classes vs extending Enumeration in Scala. They seem to offer some of the same benefits.
The advantages of using case classes over Enumerations are:
- When using sealed case classes, the Scala compiler can tell if the match is fully specified e.g. when all possible matches are espoused in the matching declaration. With enumerations, the Scala compiler cannot tell.
- Case classes naturally supports more fields than a Value based Enumeration which supports a name and ID.
The advantages of using Enumerations instead of case classes are:
- Enumerations will generally be a bit less code to write.
- Enumerations are a bit easier to understand for someone new to Scala since they are prevalent in other languages
So in general, if you just need a list of simple constants by name, use enumerations. Otherwise, if you need something a bit more complex or want the extra safety of the compiler telling you if you have all matches specified, use case classes.
One big difference is that Enumeration
s come with support for instantiating them from some name
String. For example:
public object Currency extends Enumeration {
val GBP = Value("GBP")
val EUR = Value("EUR") //etc
}
Then you can do:
val ccy = Currency("EUR")
This is useful when wishing to persist enumerations (e.g. to a database) or create them from data residing in files. However, I find in general that enumerations are a bit clumsy in scala and have the feel of an awkward add-on, so I now tend to use case object
s. A case object
is more flexible than an enum:
sealed trait Currency { def name: String }
case object EUR extends Currency { val name = "EUR" } //etc
case class UnknownCurrency(name: String) extends Currency
So now I have the advantage of...
trade.ccy match {
case EUR =>
case UnknownCcy(code) =>
}
To follow up on the other answers here, the main drawbacks of case object
s over Enumeration
s are:
Can't iterate over all instances of the "enumeration". This is certainly the case but I've found it extremely rare in practice that this is required
Can't instantiate easily from persisted value. This is also true but, except in the case of huge enumerations (e.g all currencies), this doesn't present a huge overhead
Another disadvantage of case classes versus Enumerations when you will need to iterate or filter across all instances. This is a built-in capability of Enumeration (and Java enums as well) while case classes don't automatically support such capability.
@oxbow_lakes:
Sorry, but
public object Currency extends Enumeration {
val GBP = Value("GBP")
val EUR = Value("EUR") //etc
}
val ccy = Currency("EUR")
simply doesn't work...
scala> val ccy = Currency("EUR")
<console>:5: error: type mismatch;
found : java.lang.String("EUR")
required: Int
val ccy = Currency("EUR")
^
Could somebody provide a working solution allowing construciton from String?
EDIT:
the above mentioned error is reproducible under 2.8 and was first spotted on 2.7.7 (not sure, though). To reproduce it, ommit the 'public' "keyword", as in Scala it's not a keyword :)
Corrected version, as asked by me ;) :
abstract class OxbowsEnumeration extends Enumeration {
def apply(x: String) = withName(x)
}
object Currency extends OxbowsEnumeration {
val GBP = Value("GBP")
val EUR = Value("EUR") //etc
}
val ccy = Currency("EUR")