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.