从Manifest [T]创建一个Class [T]而不进行强制转换

时间:2012-06-06 19:25:18

标签: scala type-erasure raw-types

鉴于ev: Manifest[T],我可以使用Class[T]获得ev.erasure.asInstanceOf[Class[T]]。遗憾的是ev.erasure单独返回静态类型Class[_]

我可以在没有投射的情况下从清单中获得Class[T]吗?如果没有,为什么有幸的Scala创建者在erasure方法中使用原始返回类型?

我知道这可能会对大多数代码产生微不足道的影响,但我在一个可以说是非惯用的Scala代码中遇到了这个问题,而且我对此感到好奇。

3 个答案:

答案 0 :(得分:4)

返回已删除类型的原因是,Manifest几乎总是在通用代码中使用,其中 实际上拥有该类型。这会强制您明确声明您的意图,而不是错误地假设它实际上检查该类型是您想要的类型。

您当然可以使用我的库模式来添加自己保留类型的方法:

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

答案 1 :(得分:3)

不,你必须自己做演员 - 而且应该如此。此转换可能不安全,具体取决于您要对返回的Class实例执行的操作。想象一下,我想要推出自己的演员版本:

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

这很危险 - 正如未经检查的asInstanceOf所示。为什么?因为这段代码运行良好,例如:

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

在那里,List[Int]输入为List[String]。这可以编译并运行正常,但是您可能会在代码中稍后在意外行中获得ClassCastException。这就是为什么你不能直接从Class[T]获得Manifest[T] - 因为它不安全。

答案 2 :(得分:1)

Scala的类型系统的表达力不足以为erasure提供正确的类型。正确的类型与getClass()的类型相似:

  

实际结果类型为Class<? extends |X|>,其中| X |是个   擦除getClass所在表达式的静态类型   调用。

在这种情况下,我认为正确的类型是Class[|X|](因为Manifest[T]在其类型参数中是不变的)。这与Object.getClass()不同,因为在这种情况下,静态类型和运行时类型可能不同:

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

此处,c的运行时类型为Class<Integer>,而不是Class<Number>