Since Scala 2.7.2 there is something called Manifest
which is a workaround for Java's type erasure. But how does Manifest
work exactly and why / when do you need to use it?
The blog post Manifests: Reified Types by Jorge Ortiz explains some of it, but it doesn't explain how to use it together with context bounds.
Also, what is ClassManifest
, what's the difference with Manifest
?
I have some code 开发者_JS百科(part of a larger program, can't easily include it here) that has some warnings with regard to type erasure; I suspect I can solve these by using manifests, but I'm not sure exactly how.
The compiler knows more information about types than the JVM runtime can easily represent. A Manifest is a way for the compiler to send an inter-dimensional message to the code at runtime about the type information that was lost.
It isn't clear if a Manifest would benefit the errors you are seeing without knowing more detail.
One common use of Manifests is to have your code behave differently based on the static type of a collection. For example, what if you wanted to treat a List[String] differently from other types of a List:
def foo[T](x: List[T])(implicit m: Manifest[T]) = {
if (m <:< manifest[String])
println("Hey, this list is full of strings")
else
println("Non-stringy list")
}
foo(List("one", "two")) // Hey, this list is full of strings
foo(List(1, 2)) // Non-stringy list
foo(List("one", 2)) // Non-stringy list
A reflection-based solution to this would probably involve inspecting each element of the list.
A context bound seems most suited to using type-classes in scala, and is well explained here by Debasish Ghosh: http://debasishg.blogspot.com/2010/06/scala-implicits-type-classes-here-i.html
Context bounds can also just make the method signatures more readable. For example, the above function could be re-written using context bounds like so:
def foo[T: Manifest](x: List[T]) = {
if (manifest[T] <:< manifest[String])
println("Hey, this list is full of strings")
else
println("Non-stringy list")
}
A Manifest was intended to reify generic types that get type-erased to run on the JVM (which does not support generics). However, they had some serious issues: they were too simplistic, and were unable to fully support Scala's type system. They were thus deprecated in Scala 2.10, and are replaced with TypeTag
s (which are essentially what the Scala compiler itself uses to represent types, and therefore fully support Scala types). For more details on the difference, see:
- Scala: What is a TypeTag and how do I use it?
- How do the new Scala TypeTags improve the (deprecated) Manifests?
In other words
when do you need it?
Before 2013-01-04, when Scala 2.10 was released.
Not a complete answer, but regarding the difference between Manifest
and ClassManifest
, you can find an example in the Scala 2.8 Array
paper:
The only remaining question is how to implement generic array creation. Unlike Java, Scala allows an instance creation new
Array[T]
whereT
is a type parameter. How can this be implemented, given the fact that there does not exist a uniform array representation in Java?The only way to do this is to require additional runtime information which describes the type
T
. Scala 2.8 has a new mechanism for this, which is called a Manifest. An object of typeManifest[T]
provides complete information about the typeT
.
Manifest
values are typically passed in implicit parameters; and the compiler knows how to construct them for statically known typesT
.There exists also a weaker form named
ClassManifest
which can be constructed from knowing just the top-level class of a type, without necessarily knowing all its argument types.
It is this type of runtime information that’s required for array creation.
Example:
One needs to provide this information by passing a
ClassManifest[T]
into the method as an implicit parameter:
def tabulate[T](len:Int, f:Int=>T)(implicit m:ClassManifest[T]) = {
val xs = new Array[T](len)
for (i <- 0 until len) xs(i) = f(i)
xs
}
As a shorthand form, a context bound1 can be used on the type parameter
T
instead,
(See this SO question for illustration)
, giving:
def tabulate[T: ClassManifest](len:Int, f:Int=>T) = {
val xs = new Array[T](len)
for (i <- 0 until len) xs(i) = f(i)
xs
}
When calling tabulate on a type such as
Int
, orString
, orList[T]
, the Scala compiler can create a class manifest to pass as implicit argument to tabulate.
Let's also chck out manifest
in scala
sources (Manifest.scala
), we see:
Manifest.scala:
def manifest[T](implicit m: Manifest[T]) = m
So with regards to following example code:
def foo[A](somelist: List[A])(implicit m: Manifest[A]): String = {
if (m <:< manifest[String]) {
"its a string"
} else {
"its not a string"
}
}
we can see that the manifest
function
searches for an implicit m: Manifest[T]
which satisfies the type parameter
you provide in our example code it was manifest[String]
. So when you call something like:
if (m <:< manifest[String]) {
you are checking if the current implicit m
which you defined in your function is of type manifest[String]
and as the manifest
is a function of type manifest[T]
it would search for a specific manifest[String]
and it would find if there is such an implicit.
精彩评论