Reputation: 9144
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)
.
Upvotes: 0
Views: 2063
Reputation: 16324
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.
Upvotes: 4
Reputation: 32739
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
.
Upvotes: 3