4
votes

After working in Java for a long time, I started to get interested in Scala. As a learning project, I am trying to duplicate a java library that stores and retrieves state objects from the database. For this, I would like to be able to just specify a state object like this:

@PersistName("PERSON") case class  Person extends Entity {
  @Persist var id:Long = -1
  @Persist @MaxLength(80) var firstName = ""
  @Persist @MaxLength(80) var lastName = ""
  @Persist var gender = Gender.Male
  @Persist @MaxLength(80) var userName  = ""
  @Persist @OptionClass(classOf[Date]) var birthDay:Option[Date] = None
}

The code to serialize/un-serialize an instance of Person uses reflection to know the types of fields and works ok for all but the gender field. The gender field is an Enumeration that is defined as:

object Gender extends Enumeration {
  type Gender = Value
  val Male,Female,Unknown = Value
}

The problem is that I don’t know how I can use reflection too create a new Gender value using only the Person class.

4
Why the "using only the Person class"? - Blaisorblade

4 Answers

4
votes

Scala's Enumeration is interesting, but case classes frequently have an edge over it:

sealed class Gender
case object Male extends Gender
case object Female extends Gender

This have the advantage of being matchable, and Scala will even complain if you test for one gender but not the other. And, it seems to slve your problem more easily. :-)

0
votes

You can use Gender.Male.id to get an Int presentation of male gender value. Use Gender.apply() to get it back:

val person = Person()

println("gender = " + person.gender.id)
// prints "gender = 0" on my mac

person.gender = Gender(Gender.Female.id) // have a little operation
println("gender = " + person.gender.id)
// prints "gender = 1" on my mac

Since you are handling the persistence bit, all you need to do is to store the Int representation of gender when serializing and restore it back to Gender when deserializing.

Use a custom annotation on the Enumeration fields if you want to generalize the solution.

0
votes

I have run into the same problem and found a rather bulky solution but it works. You need to persist the name of the enclosing Enumeration since there is no way to find out the real Enumeration from a Value using reflection.

object WeekDay extends Enumeration {
  type WeekDay = Value
  val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value
}

val day = WeekDay.Fri

val className = day.getClass.getField("$outer").get(day).getClass.getCanonicalName

// write to database
// className // String
// day.id  // Int

Now when you read out from the database

// read from database
// obtain className and id

// enumObject will be WeekDay
val enumObject = Class.forName(className).getField("MODULE$").get().asInstanceOf[Enumeration]

// value will be WeekDay.Fri
val value = enumObject(id)
0
votes

Check out my EnumReflector:

You need to supply it the scala type of the field.

val enumObject:Type = ... object's scala.Enumeration field
val typ:Type = ... object's scala.Enumeration field's scala type

val isEnum = EnumReflector.isEnumeration(typ)

val reflector = EnumReflector(typ)
val eid = reflector.toID(enumObject)
val enum = reflector.fromID(eid)
assertTrue(eid eq enum)

There's a unit test in the project that demonstrates it.

Also, see my expand version of this answer here: Is it possible to use reflection to find the actual type of a field declared to be a Scala Enumeration subtype?