Mapping Java Enums in Slick

We had some Scala code that depended on a bunch of Java enums, and I was adding some support for mapping them to Slick columns. (Side note: so far I think Slick is a great technology.) One of the many handy features of Slick is that you can use a type class to define a mapping between some arbitrary type and a database-friendly type. For example, if you have a Java enum “SomeEnum”, defining a mapping is as simple as creating this implicit value:

implicit val SomeEnumMapper = MappedColumnType.base[SomeEnum, String](_.name, SomeEnum.valueOf _)

Now you can use it:

def myColumn = column[SomeEnum]("myColumn") // maps to a String column

But suppose you have lots of enums, where “lots of” is an integer greater than one. You can write a generic enum mapper “generator” that pops out an enum mapper on demand, i.e. whenever the compiler needs one. This is a great case for implicit def: as you would expect, val is for when you have just one instance, and def will generate a new instance for each invocation. The tricky bit is the reverse mapping (SomeEnum.valueOf _ above) since you can’t write A.valueOf _ for a type parameter A. Instead we can use a bit of Reflection black magic to summon a Class[A], then stuff that into the generic java.lang.Enum.valueOf(Class, String) method:

implicit def JavaEnumMapper[A <: java.lang.Enum[A]](implicit classTag: ClassTag[A]) = MappedColumnType.base[A, String](_.name, { x => java.lang.Enum.valueOf(classTag.runtimeClass.asInstanceOf[Class[A]], x) })

Or the sugary version using a context bound (about 5 characters shorter… yay?):

implicit def JavaEnumMapper[A <: java.lang.Enum[A] : ClassTag] = MappedColumnType.base[A, String](_.name, { x => java.lang.Enum.valueOf( implicitly[ClassTag[A]].runtimeClass.asInstanceOf[Class[A]], x) })

Now we can write column[AnyEnum] for any Java enum type.

Posted in Uncategorized