0

For example I have code looks like this:

class Parent
class Child1 extends Parent
class Child2 extends Parent

class Foo {

  def retrieve(arg: String): List[Parent] = {
    arg match {
      case "Child1" => get[Child1]()
      case "Child2" => get[Child2]()
    }
  }

  def get[T: Manifest](): List[T] = ...

}

In the retrieve method, I want to simplify the code into one get method call only like this:

  def retrieve(arg: String): List[Parent] = {
    val t = arg match {
      case "Child1" => ?
      case "Child2" => ?
    }

    get[t]()
  }

Is it possible to achieve this in scala?

UPDATE:

I tried the solution from the answer here but I got a problem, it doesn't work with overloaded get method, for example:

def get[T: Manifest](x: String): List[T] = ...

def get[T: Manifest, U: Manifest](x: String): List[(T, U)] = ...

For example, in the retrieve:

val t = arg match {
  case "Child1" => manifest[Child1]
  case "Child2" => manifest[Child2]
}

get("test")(t)

I got ambiguous reference to overloaded definition compile error on the line get("test")(t).

null
  • 8,669
  • 16
  • 68
  • 98
  • possible duplicate of [How do I get around type erasure on Scala? Or, why can't I get the type parameter of my collections?](http://stackoverflow.com/questions/1094173/how-do-i-get-around-type-erasure-on-scala-or-why-cant-i-get-the-type-paramete) – johanandren Apr 14 '15 at 11:54
  • 2
    That is not a duplicate of this question, he is already using a Manifest. – Régis Jean-Gilles Apr 14 '15 at 12:03
  • 2
    And he does not want to know about how to get around type erasure he wants the to know the implementation of get function in some other way. – curious Apr 14 '15 at 12:05
  • Your update is really a new question. I think you should ask it in a new question! – Ben Reich Apr 14 '15 at 16:34

2 Answers2

4

Manifest is basically deprecated. :

In Scala 2.10, scala.reflect.ClassManifests are deprecated, and it is planned to deprecate scala.reflect.Manifest in favor of TypeTags and ClassTags in an upcoming point release. Thus, it is advisable to migrate any Manifest-based APIs to use Tags.

You should consider using the more modern ClassTag or TypeTag. In this case, ClassTag works better (since TypeTags can't be used in pattern matching):

def retrieve(arg: String): List[Parent] = {
    val t = arg match {
      case "Child1" => classTag[Child1]
      case "Child2" => classTag[Child2]
    }

    get(t)
}

def get[T : ClassTag]: List[T] = list collect { 
    case x: T => x 
}

You can read more about ClassTags, TypeTags, and their relationship to Manifest in the docs here.

In case it's not clear, this works because the type constraint on T is a context bound, meaning the method signature of get is equivalent to:

def get[T](implicit ev: ClassTag[T]): List[T]

So, when we call get(t), we're explicitly specifying the implicit parameter. Read more about context bounds here.

If the context bound or implicit parameter is confusing, you can also achieve your goals by making get non-generic:

def get(c: ClassTag[_]) = list collect { 
    case x if ClassTag(x.getClass) == c => x 
}

This non-generic, non-implicit version might help you resolve your overloading issue.

Ben Reich
  • 16,222
  • 2
  • 38
  • 59
  • Thanks. I wonder if the `get` method doesn't have `: Manifest` or `ClassTag` on the `[T]`, is it still possible to do type parameter passing? – null Apr 14 '15 at 15:57
  • Please see the **UPDATE** section on the question post, I got `ambiguous reference error`. – null Apr 14 '15 at 16:27
  • @suud I've added an update which allows you to make `get` non-generic. As for your update, I feel like that's a new question! – Ben Reich Apr 14 '15 at 16:37
  • By make it non-generic non-implicit, did you mean unify the overloaded `get` methods into a single `get` method? FYI, in my case I don't have option to change the `get` methods. – null Apr 14 '15 at 21:00
3

Your question boils down to how to retrieve the Manifest of a given type. This can be done using the manifest method. Then you can explictly pass the manifest, to get.

class Foo {
  def retrieve(arg: String): List[Parent] = {
    val t = arg match {
      case "Child1" => manifest[Child1]
      case "Child2" => manifest[Child2]
    }

    get(t)
  }

  def get[T <: Parent: Manifest]: List[T] = ...
}

As a side note, you should probably use a map to retrieve the manifests (rather than pattern matching) so as to make it more easily editable, or possibly at one point replacing the hard-coded list of types with some init-time computation:

object Foo {
  private val manifestByName = Map[String, Manifest[_<:Parent]](
    "Child1" -> manifest[Child1],
    "Child2" -> manifest[Child2]
  )
}
class Foo {
  def retrieve(arg: String): List[Parent] = {
    val t = Foo.manifestByName.getOrElse(arg, sys.error(s"Invalid class name $arg"))
    get(t)
  }

  def get[T <: Parent: Manifest]: List[T] = { println(manifest[T]); Nil }
}

Finally, note that Manifest is now deprecated, it was superseded with ClassTag\ TypeTag.

Régis Jean-Gilles
  • 32,541
  • 5
  • 83
  • 97
  • Thanks. I wonder if the `get` method doesn't have `: Manifest` or `ClassTag` on the `[T]`, is it still possible to do type parameter passing? – null Apr 14 '15 at 15:57
  • Please see the **UPDATE** section on the question post, I got `ambiguous reference error`. – null Apr 14 '15 at 16:27