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

别说谁变了你拦得住时间么 提交于 2019-12-24 07:43:06

问题


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.


回答1:


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.




回答2:


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:


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>.



来源:https://stackoverflow.com/questions/10920820/creating-a-classt-from-a-manifestt-without-casting

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!