Call a method based on template type in Scala

generics, scala

Solution

You can use the same technique as the `CanBuildFrom` trait of collections.

We first create a `Getter` case class

case class Getter[T](getter: (ContentValues, String) => T) {

  def getOpt(contentValues: ContentValues, key: String): Option[T] =
    if (contentValues containsKey key) Some(getter(contentValues, key))
    else None
}

This allows us to create a `ContentValues` wrapper that has the desired method.

implicit class ContentValuesWrapper(val c: ContentValues) extends AnyVal {
  def getAsOpt[T](key: String)(implicit getter: Getter[T]) =
    getter.getOpt(c, key)
}

Now, in order to call the `getAsOpt` method on `ContentValues` we need to provide `implicit` instances of `Getter` for the correct types.

object Getter {
  implicit val intGetter = Getter(_ getAsInteger _)
  implicit val longGetter = Getter(_ getAsLong _)
  implicit val stringGetter = Getter(_ getAsString _)
}

Now you can use the `getAsOpt` method on a `ContentValues` instance.

// fake version of ContentValues
val c = 
  new ContentValues {
    val m = Map("a" -> "1", "b" -> "2", "c" -> "3")

    def getAsInteger(k: String): Int = getAsString(k).toInt
    def getAsLong(k: String): Long = getAsString(k).toLong
    def getAsString(k: String): String = m(k)

    def containsKey(k: String): Boolean = m contains k
  }

c.getAsOpt[Int]("a")      //Option[Int] = Some(1)
c.getAsOpt[Long]("b")     //Option[Long] = Some(2)
c.getAsOpt[String]("c")   //Option[String] = Some(3)
c.getAsOpt[Int]("d")      //Option[Int] = None
c.getAsOpt[Long]("e")     //Option[Long] = None
c.getAsOpt[String]("f")   //Option[String] = None

Problem

I'm trying to figure out a way to call a Java API from Scala. Basically, there's a `ContentValues` object that has a couple of methods like `getAsString`, `getAsLong`, each having their own different return type. Can I wrap `ContentValues` in another object so that I can add a `get[T]` method that calls the correct `getAsXXX` method depending on T? What I tried (didn't work, complains about ambiguous implicit resolution) : ``` object SContentValuesConversions { case class SContentGetter[ScalaType](val check: String => Boolean, val getter: String => ScalaType) { def getTyped(key: String): Option[ScalaType] = { if (!check(key)) None else Some(getter(key)) } } implicit def SIntContentValues(cv: ContentValues) = SContentGetter((cv containsKey _), (cv getAsInteger _)) implicit def SLongContentValues(cv: ContentValues) = SContentGetter((cv containsKey _), (cv getAsLong _)) implicit def SStringContentValues(cv: ContentValues) = SContentGetter((cv containsKey _), (cv getAsString _)) } ```

Original source