Let me explain ;-)
both classes below are in package com.company.foo
RoleGroup.scala
abstract class RoleGroup
case object ADMIN extends RoleGroup
case object MEMBER extends RoleGroup
MailSender.scala
abstract class MailSender
case object ADMIN extends MailSender
case object STAFF extends MailSender
case object ACCOUNTANT extends MailSender
The problem here is that ADMIN is ambiguous since there is no namespace separation with case objects. It seems there can only be one uniquely named case object per package.
I suppose I could name case objects based on function a la mailADMIN, roleADMIN and so on. Or should I just create proper Enums and forget about case objects? Or take some other approach?
A case object is like an object , but just like a case class has more features than a regular class, a case object has more features than a regular object. Its features include: It's serializable. It has a default hashCode implementation. It has an improved toString implementation.
Case classes work great for data transfer objects, the kind of classes that are mainly used for storing data, given the data-based methods that are generated.
A case object, on the other hand, does not take args in the constructor, so there can only be one instance of it (a singleton like a regular scale object is). A case object is a singleton case class.
In Scala, an object is a named instance with members such as fields and methods. An object and a class that have the same name and which are defined in the same source file are known as companions. Companions has special access control properties, which is covered under Scala/Access modifiers.
you could do something like:
object RoleGroup {
sealed trait RoleGroup
case object ADMIN extends RoleGroup
case object MEMBER extends Rolegroup
}
and similarly for MailSender
. Then in situations where you're only using one, you can do import MailSender._
or vise versa, but when using both you refer to them as RoleGroup.ADMIN
, etc.
Whether you want to take this approach or use Enums mostly depends on how you intend to use them. In this aproach, each "enum" is a type, whereas with Enums each enum is a value. The former works better for pattern matching since the compiler can check if your matches are exhaustive, the latter is better (IMO) for working with serialization.
You don't need enums here, as much as probably anywhere else. All you need is proper namespacing. I find the companion object approach to be the most benefitial:
sealed abstract class RoleGroup
object RoleGroup {
case object Admin extends RoleGroup
case object Member extends RoleGroup
// also just in case
sealed case class SomeParameterizedGroup (
param1: Any,
param2: Int
) extends RoleGroup
}
Using it this way will very much remind you of Java's enums, while also providing you with strengths of Scala:
def foo (roleGroup: RoleGroup) =
roleGroup match {
case RoleGroup.Admin =>
case RoleGroup.SomeParameterizedGroup(param1, param2) =>
}
Please note that in Dan's solution the type of roleGroup
would be RoleGroup.RoleGroup
, which kinda feels unnatural. Also note that uppercase names violate Scala's style conventions and from examples above you can kinda see why.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With