Creating a Class[T] from a Manifest[T] without cas

2019-07-30 18:50发布

Given an ev: Manifest[T] I can get a Class[T] using ev.erasure.asInstanceOf[Class[T]]. It's a shame that ev.erasure alone returns a static type of Class[_].

Can I get a Class[T] from a manifest without casting? If not is there a reason why the blessed Scala creators have gone for a raw return type in the erasure method?

I understand this may have a negligible impact on most code but I've run into this issue in an arguably non-idiomatic piece of Scala code and am curious more than anything else.

3条回答
【Aperson】
2楼-- · 2019-07-30 19:05

The reason for returning the erased type is that Manifest is almost always used in generic code where you don't actually have the type in hand. This forces you to explicitly declare your intentions instead of mistakenly assuming that it actually checks that the type is the one you want.

You can of course use the enrich my library pattern to add your own method that preserves types:

class ClassFriendlyManifester[T](m: Manifest[T]) {
  def toClass = m.erasure.asInstanceOf[Class[T]]
}
implicit def manifests_like_classes[T](m: Manifest[T]) = new ClassFriendlyManifester(m)

def example[T: Manifest] = implicitly[Manifest[T]].toClass

scala> example[String]
res2: Class[String] = class java.lang.String

def example2[T](implicit ev: Manifest[T]) = ev.toClass

scala> example2[String]
res5: Class[String] = class java.lang.String
查看更多
何必那么认真
3楼-- · 2019-07-30 19:08

No, you have to do the cast yourself — and it should be so. This cast is potentially unsafe, depending on what you want to do with the returned Class instance. Imagine I want to roll in my own version of a cast:

def cast[T](obj: Any)(implicit m: Manifest[T]) =
  m.erasure.asInstanceOf[Class[T]].cast(obj)

This is dangerous — as indicated by the unchecked asInstanceOf. Why? Because this code runs fine with such nonsense, for instance:

val listInt = List(1, 2, 3)
val listString = cast[List[String]](listInt)

There, a List[Int] typed as a List[String]. And this compiles and runs fine, but you'll probably get a ClassCastException later in your code at an unexpected line. That's why you cannot directly get a Class[T] from a Manifest[T] — because it is unsafe.

查看更多
我欲成王,谁敢阻挡
4楼-- · 2019-07-30 19:08

Scala's typesystem isn't expressive enough to give the correct type for erasure. The correct type would be similar to the one for getClass():

The actual result type is Class<? extends |X|> where |X| is the erasure of the static type of the expression on which getClass is called.

In this case, I think the correct type would be Class[|X|] (because Manifest[T] is invariant in its type parameter). This is different to Object.getClass(), because in that case the static type and runtime type could be different:

Number n = 0; 
Class<? extends Number> c = n.getClass();

Here, the runtime type of c would be Class<Integer>, not Class<Number>.

查看更多
登录 后发表回答