4

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?

Nagarjuna Pamu
  • 14,737
  • 3
  • 22
  • 40
sam Wong
  • 41
  • 2
  • The thing is that all type-parameters are erased at runtime (this is called `type erasure`) and that is the reason for type-parameters being ignored for determining the signature of a method. – sarveshseri Nov 01 '16 at 09:58
  • Possible duplicate of [Scala: Method overloading over generic types](http://stackoverflow.com/questions/4982552/scala-method-overloading-over-generic-types) – Yuval Itzchakov Nov 01 '16 at 10:05
  • 1
    @YuvalItzchakov pamu's answers is better choice for this question. – sarveshseri Nov 01 '16 at 10:19
  • 1
    Someone came and down-voted all the answers on this question (even pamu's which looks like a great answer to me) without a comment. That is just awesome. – sarveshseri Nov 01 '16 at 13:25

3 Answers3

2

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
Nagarjuna Pamu
  • 14,737
  • 3
  • 22
  • 40
0

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) {
        ...
      }
    }
  }
}
sarveshseri
  • 13,738
  • 28
  • 47
  • I have try your code, that's helpful too! And by this way, I don't need import other package, These is better answer! – sam Wong Nov 01 '16 at 11:59
  • Actually @pamu's answer will be better for this particular problem. And `scala.reflect.runtime.universe` is part of `scala` itself. So you are not using anything external. – sarveshseri Nov 01 '16 at 13:24
0

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.

  • I have try this way, found that this is fine for defined a function, but I cannot use this function, because the input "params" should be "Seq[Either]" type. thx all the same~ – sam Wong Nov 01 '16 at 11:56