How do I create an enum in scala that has an extra field

Viewed 10936

In Java I have something like this

public enum FlatFileHeaderMapping {

   HEADER_EL(1),
   HEADER_RESERVED1(5),
   HEADER_RESERVED2(2),
   HEADER_MESSAGE_TYPE(4)

   public final int fieldSize;

    private FlatFileHeaderMapping(int fieldSize) {
        this.fieldSize = fieldSize;
   }

}

which I can then use it place each line into a map and later access the keys in the map via this enum (like symbols)

Enumeration does not have this quality as far as I can see, and case classes are not ordered like the enum declarations - so cannot be used to match a record layout as shown above. At least not without the support of an ordered collection.

I could be missing something obvious, hence the question!

Thanks

Ray

5 Answers

Reproducing the contents of the accepted answer, as it's hidden behind a broken Tumblr link (that I accessed via Archive.org), which in turn points to this page.

trait Enum { //DIY enum type
  import java.util.concurrent.atomic.AtomicReference //Concurrency paranoia

  type EnumVal <: Value //This is a type that needs to be found in the implementing class

  private val _values = new AtomicReference(Vector[EnumVal]()) //Stores our enum values

  //Adds an EnumVal to our storage, uses CCAS to make sure it's thread safe, returns the ordinal
  private final def addEnumVal(newVal: EnumVal): Int = { import _values.{get, compareAndSet => CAS}
    val oldVec = get
    val newVec = oldVec :+ newVal
    if((get eq oldVec) && CAS(oldVec, newVec)) newVec.indexWhere(_ eq newVal) else addEnumVal(newVal)
  }

  def values: Vector[EnumVal] = _values.get //Here you can get all the enums that exist for this type

  //This is the trait that we need to extend our EnumVal type with, it does the book-keeping for us
  protected trait Value { self: EnumVal => //Enforce that no one mixes in Value in a non-EnumVal type
    final val ordinal = addEnumVal(this) //Adds the EnumVal and returns the ordinal

    def name: String //All enum values should have a name

    override def toString = name //And that name is used for the toString operation
    override def equals(other: Any) = this eq other.asInstanceOf[AnyRef]
    override def hashCode = 31 * (this.getClass.## + name.## + ordinal)
  }
}

//And here's how to use it, if you want compiler exhaustiveness checking
object Foos extends Enum {
  sealed trait EnumVal extends Value /*{ you can define your own methods etc here }*/

  val F = new EnumVal { val name = "F" }
  val X = new EnumVal { val name = "X" }
}

/**
scala> Foos.values.find(_.name == "F")
res3: Option[Foos.EnumVal] = Some(F)

scala> Foos.X.ordinal
res4: Int = 1

scala> def doSmth(foo: Foos.EnumVal) = foo match {
  case Foos.X => println("pigdog")
}

<console>:10: warning: match is not exhaustive!
missing combination        $anon$1
missing combination        $anon$2

scala> def doSmth(foo: Foos.EnumVal) = foo match {
         case Foos.X => println("pigdog")
         case Foos.F => println("dogpig")
       }
doSmth: (foo: Foos.EnumVal)Unit
**/

//But if you don't care about getting exhaustiveness warnings, you can do:

object Foos extends Enum {
  case class EnumVal private[Foos](name: String) extends Value /* { you can define your own methods and stuff here } */

  val F = EnumVal("F")
  val X = EnumVal("X")
}

/**
Which is a bit less boilerplatey.


Cheers,
√
**/
Related