19

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

Mond Raymond
  • 320
  • 1
  • 4
  • 9
  • There are other questions with good answers here: http://stackoverflow.com/questions/1898932/case-classes-vs-enumerations-in-scala/4541557#4541557 and here: http://stackoverflow.com/questions/1321745/scala-doesnt-have-enums-what-to-use-instead-of-an-enum – Viktor Hedefalk Jun 24 '11 at 21:49
  • Seeing the answers so far, I should also note that there is another advantage of the Java enum over case objects in that they retain the order of declaration. This is an interesting property if once wants to match the layout of a record. IIANM case objects would have to be placed into a linked list to share that property. – Mond Raymond Jun 26 '11 at 17:38
  • Viktor has produced the best answer so far. Although it is more verbose than the Java version, it is more flexible. – Mond Raymond Aug 01 '11 at 18:58

5 Answers5

16

overthink is right, but there's a less verbose way of declaring the case objects:

sealed abstract class FlatFileHeaderMapping(val fieldSize: Int)
case object HEADER_EL extends FlatFileHeaderMapping(1)
case object HEADER_RESERVED1 extends FlatFileHeaderMapping(5)
case object HEADER_RESERVED2 extends FlatFileHeaderMapping(2)
case object HEADER_MESSAGE_TYPE extends FlatFileHeaderMapping(4)
prat_c
  • 276
  • 2
  • 5
  • 1
    I like this answer since the notation is less verbose. I am still however a little disturbed as this feels hacky compared to the Java implementation - Java is less verbose and has a nicer feel. – Mond Raymond Jun 26 '11 at 17:27
  • i have edited the question to add the fact that such objects are not ordered like enum declarations. Since I would like to use Scala to deal with legacy files and record layouts I would like an equal or better notation. Thanks for the answer so far and I would be interested if you had any further thoughts. Cheers – Mond Raymond Jun 26 '11 at 17:43
  • Yeah, I like Scala overall, but for some things like this it gets a bit ugly. I'm still learning Scala myself, I don't really know of any better way yet. – prat_c Jun 29 '11 at 02:29
  • the key here is the sealed **abstract class** instead of trait - that allows to have parameters to the enum – WestCoastProjects Jul 01 '15 at 20:52
5

You could try using case objects:

sealed trait FlatFileHeaderMapping { val fieldSize: Int }                                                                                                                                                                          
case object HEADER_EL extends FlatFileHeaderMapping { val fieldSize = 1 }                                                                                                  
case object HEADER_RESERVED1 extends FlatFileHeaderMapping { val fieldSize = 5 }                                                                                           
case object HEADER_RESERVED2 extends FlatFileHeaderMapping { val fieldSize = 2 }                                                                                           
case object HEADER_MESSAGE_TYPE extends FlatFileHeaderMapping { val fieldSize = 4 } 

You can then use the enum like so:

object Test {                                                                                                                                                              
  def foo(x: FlatFileHeaderMapping) {                                                                                                                                      
    val result =                                                                                                                                                           
      x match {
        case HEADER_EL => "it's a HEADER_EL!"                                                                                                                              
        case other => "its field size is: " + other.fieldSize                                                                                                             
      }                                                                                                                                                                    
    println(result)                                                                                                                                                        
  }                                                                                                                                                                        

  def main(args: Array[String]) {                                                                                                                                          
    foo(HEADER_EL)                                                                                                                                                         
    foo(HEADER_MESSAGE_TYPE)                                                                                                                                               
  }                                                                                                                                                                        
}

The main nicety you get here is compile-time checking that all enum values are handled. i.e in the x match { ... } code above you'd get a compile error if you didn't have the 'case other => ...` clause in there.

I'm pretty much just restating this answer, which lists pros and cons of this approach.

Community
  • 1
  • 1
overthink
  • 23,985
  • 4
  • 69
  • 69
  • I accept this is a correct answer. I am however still disattisfied if this is the best we can do in Scala (not blaiming you of course!). In this case (and it's one of the few that I am aware of) Java shows itself to be more concise and obvious. These properties are usually in Scala's favour, hence my lack of enthusiasm. I appreciate your effort to respond. – Mond Raymond Jun 26 '11 at 17:33
  • Correction - Viktor has improved matters – Mond Raymond Aug 01 '11 at 19:00
1
object Direction extends Enumeration {
  val North = Value("North")
  val East = Value("East")
  val South = Value("South")
  val West = Value("West")
}

scala> import Direction._
scala> values foreach println
scala> val map = HashMap(North -> 1, South -> 2)
agilesteel
  • 16,775
  • 6
  • 44
  • 55
  • problem with this is that I cannot define the numeric value. Enums in Scala have an index and a value by default. It's not possible to add a duplicate index (for good reasons)... thus I need a good way to add an extra property to the Enumeration – Mond Raymond Jun 26 '11 at 17:25
0

This is answered in Enumeration with constructor and lookup table

A simpler solution exist for integer value:

object FlatFileHeaderMapping extends Enumeration  {
   type FlatFileHeaderMapping = Value 
   val HEADER_EL = Value(1, "HEADER_EL")
   val HEADER_RESERVED1 = Value(5, "HEADER_RESERVED1")
   val HEADER_RESERVED2 = Value(2, "HEADER_RESERVED2")
   val HEADER_MESSAGE_TYPE = Value(4, "HEADER_MESSAGE_TYPE")
}
Community
  • 1
  • 1
Basilevs
  • 22,440
  • 15
  • 57
  • 102
0

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,
√
**/
Jeff Evans
  • 1,257
  • 1
  • 15
  • 31