Reputation: 65
I have the following nested structure involving type parameters and type members:
trait B
trait B1 extends B
trait U {
type _B <: B
}
type U1 = U {
type _B = B1
}
class Q[_U <: U] {
override def toString() : String = {
// print out type information on B here...
}
}
def test() {
val q = new Q[U1]()
println(q.toString())
}
It seems impossible to me gathering the type information on B at runtime because of the way U1 is specified.
Am I wrong? If not, is there a solution with minor changes in the setup?
Thanks to the answer from Kipton Barros I came up with the following setup:
trait B
trait B1 extends B
trait B2 extends B
trait U {
type _B <: B
implicit val mfB : Manifest[_B]
}
class U1 extends U {
type _B = B1
val mfB : Manifest[_B] = implicitly
}
class U2 extends U {
type _B = B2
val mfB : Manifest[_B] = implicitly
}
class Q[_U <: U](u : _U) {
override def toString() : String = {
"B: " + u.mfB.erasure.getName()
}
}
def test() {
println(new Q(new U1) toString)
println(new Q(new U2) toString)
}
The only downside of this approach is the need for instantiation of U.
Upvotes: 2
Views: 335
Reputation: 21112
I would have thought to use a combination of a type refinement and a Manifest. The former allows to surface the abstract type _B
as a type parameter B
, and the latter instructs the Scala compiler to reify the type of B
(edit: the static type from the call context) as a run-time object. Here's my attempt,
trait B
trait B1 extends B
trait B2 extends B
trait U { type _B <: B }
class U1 extends U { type _B = B1 }
class Q[B: Manifest, _U <: U { type _B = B}](u: U) {
override def toString() : String = {
implicitly[Manifest[B]].toString // Manifest[B] was an implicit parameter to Q
}
}
// Four tests:
println(new Q[B1, U1](new U1) toString) // (1) prints "$line1.$read$$iw$$iw$B1"
// println(new Q[B2, U1](new U1) toString) // (2) correctly fails to compile
// println(new Q[Nothing, U1](new U1) toString)// (3) correctly fails to compile
println(new Q(new U1) toString) // (4) prints "Nothing" (why not B1?)
It works in the first case, where explicit type parameters are given. The second case correctly fails to compile, since U1
contains a B1
type, not a B2
type. Similarly for the third case. For some reason, though, the Scala compiler is generating an incorrect manifest in the fourth case, even though the compiler seems to infer type B1
. I don't know enough to say whether this is a bug, but it's certainly surprising to me. Can anyone explain why case (4) doesn't print B1's Manifest?
Upvotes: 0
Reputation: 55028
Just to clear up a misconception: a Manifest
does not carry the run-time type of a type parameter. It carries the static type from the context of the call site from which the method or constructor that requires a manifest is called.
scala> def foo[A: Manifest](a: A) = (manifest[A].erasure, a.asInstanceOf[AnyRef].getClass)
foo: [A](a: A)(implicit evidence$1: Manifest[A])(java.lang.Class[_], java.lang.Class[_])
scala> foo("")
res1: (java.lang.Class[_], java.lang.Class[_]) = (class java.lang.String,class java.lang.String)
scala> foo[AnyRef]("")
res2: (java.lang.Class[_], java.lang.Class[_]) = (class java.lang.Object,class java.lang.String)
scala> val a: Any = ""
a: Any = ""
scala> foo(a)
res3: (java.lang.Class[_], java.lang.Class[_]) = (class java.lang.Object,class java.lang.String)
Upvotes: 2