sam Wong
sam Wong

Reputation: 41

How can I overload functions with generic parameters in scala

I just want to overload seq in a function, like:

insertBatch(sql: String, params: Seq[Seq[String]])
insertBatch(sql: String, params: Seq[Map[String,String]])

But always hint me that "insertBatch(String, Seq) is already defined in scope". So I try to use "Any":

insertBatch(sql: String, params: Seq[Any])

This can be defined, but how can I use this params in the function? such as:

def insertBatch(sql: String, params: Seq[Any]){
    ......
    for( param <- params) {
        // when param is a map?
        for( p <- param) {
            ...
        }
        // when param is a seq? 
        param.get("some Key")
        ...
    }
    ......
}

Scala is just a new language for me, Any help?

Upvotes: 4

Views: 1721

Answers (3)

Nagarjuna Pamu
Nagarjuna Pamu

Reputation: 14825

Because of the type erasure of the JVM the above two methods are indistinguishable by the JVM at runtime. The general way to deal with type erasure issues is TypeTag. You may use classTag as well but classTag is limited.

So, Instead of declaring two methods, declare one method with type parameter T and at runtime figure out what T is and proceed.

  import scala.reflect.runtime.universe._

  def insertBatch[T: TypeTag](sql: String, params: Seq[T]): Unit = typeOf[T] match {
    case a if  a =:= typeOf[Seq[String]] =>
      val l = params.asInstanceOf[Seq[Seq[String]]]
      // do something here
    case b if b =:= typeOf[Map[String, String]] =>
      val l = params.asInstanceOf[Seq[Map[String, String]]]
      // do something here
    case _ => //some other types
  }

Scala REPL

scala> :paste
// Entering paste mode (ctrl-D to finish)

import scala.reflect.runtime.universe._

  def insertBatch[T: TypeTag](sql: String, params: Seq[T]): Unit = typeOf[T] match {
    case a if  a =:= typeOf[Seq[String]] =>
      val l = params.asInstanceOf[Seq[Seq[String]]]
      println("bar")
    case b if b =:= typeOf[Map[String, String]] =>
      val l = params.asInstanceOf[Seq[Map[String, String]]]
      println("foo")
    case _ => println("ignore")
  }



// Exiting paste mode, now interpreting.

import scala.reflect.runtime.universe._
insertBatch: [T](sql: String, params: Seq[T])(implicit evidence$1: reflect.runtime.universe.TypeTag[T])Unit

scala> insertBatch[Seq[String]]("", Seq(Seq("")))
bar

scala> insertBatch[Map[String, String]]("", Seq(Map.empty[String, String]))
foo

scala> insertBatch[String]("", Seq(""))
ignore

Upvotes: 2

user1804599
user1804599

Reputation:

You should use a discriminated union instead of Any, for example:

def insertBatch[T](sql: String, params: Seq[Either[Seq[String], Map[String, String]]]) =
  params foreach {
    case Left(seq) => ...
    case Right(map) => ...
  }

This makes clear your intent in the type, doesn't suffer from type erasure issues, and doesn't violate parametricity.

Upvotes: 0

sarveshseri
sarveshseri

Reputation: 13985

The thing is that all type parameters are erased at runtime (type erasure) hence type parameters are ignored ored for determining the signature of a method.

Now lets comeback to your requirement, you want your function to behave differently for Seq[Seq[String]] and Seq[Map[String,String]]. You will have to change it in a way so that the "type hint" is included by the compiler.

def insertBatch[T](sql: String, params: Seq[T]){
  for( param <- params) {
    param match {
      // when param is a map?
      case _: Map[_, _] => for(key <- param.keys) {
        ...
      }
      // when param is a seq?
      case _: Seq[_] => for( p <- param) {
        ...
      }
    }
  }
}

Upvotes: 0

Related Questions