带有参数化类型的Scala asInstanceOf

带有参数化类型的Scala asInstanceOf,scala,Scala,我想编写一个强制转换为类型a的函数,其中a可以是例如List[Int]或更复杂的参数化类型,如Map[Int,List[Int]] def castToType[A](x: Any): A = { // throws if A is not the right type x.asInstanceOf[A] } 现在,由于类型擦除(我相信),即使类型不正确,代码也能正常工作。该错误仅在访问时显示,带有ClassCastException val x = List(1, 2, 3) val

我想编写一个强制转换为类型a的函数,其中a可以是例如List[Int]或更复杂的参数化类型,如Map[Int,List[Int]]

def castToType[A](x: Any): A = {
  // throws if A is not the right type
  x.asInstanceOf[A]
}
现在,由于类型擦除(我相信),即使类型不正确,代码也能正常工作。该错误仅在访问时显示,带有ClassCastException

val x = List(1, 2, 3)
val y = castToType[List[String]](x)
y(0) --> throws java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String

有没有一种方法可以使用清单使其正常工作?谢谢

是的,问题是由类型擦除引起的。如果你尝试

val x = List(1,2,3)
val y = castToType[Int](x)
正如预期的那样,立即抛出异常。当尝试强制转换到
Array[String]
或甚至
Array[Int]
时,也会发生同样的情况

我不认为您可以创建一个通用类型转换器,它可以在集合和其他对象中使用类型。您需要为每个对象类型创建一个转换器。例如:

def castToType[A](x: List[A]) = x.map(i => i.asInstanceOf[A])

不幸的是,这是
作为安装的固有限制。事实上,我很惊讶地看到scaladoc在:

请注意,运行时强制转换的成功在于modulo Scala的擦除语义。因此,表达式
1.asInstanceOf[String]
将在运行时抛出
ClassCastException
,而表达式
List(1).asInstanceOf[List[String]
将不会。在后一个示例中,由于类型参数作为编译的一部分被删除,因此无法检查列表的内容是否属于请求的类型

如果您主要关心的是在可遍历对象的错误转换上快速失败,这可能是从DB/memcached接口取回内容时的主要问题,那么我正在尝试强制可遍历对象的头部转换:

def failFastCast[A: Manifest, T[A] <: Traversable[A]](as: T[A], any: Any) = { 
  val res = any.asInstanceOf[T[A]]
  if (res.isEmpty) res 
  else { 
    manifest[A].newArray(1).update(0, res.head) // force exception on wrong type
    res
  }
}
但在更复杂的问题上,

val x = Map(1 -> ("s" -> 1L)): Any
failFastCast(Map[Int, (String, String)](), x) // no throw

我想知道是否有一种方法可以递归地深入到一个列表中,以保持强制转换,直到没有更多的类型参数…

您确实是正确的-类型擦除意味着您不能以这样的方式“强制转换”以区分
List[Int]
List[String]
。但是,您可以改进您正在执行的演员阵容,即删除
A
,使您无法区分
Int
字符串

def cast[A](a : Any) = a.asInstanceOf[A]
//... is erased to
def erasedCast(a : Any) = a.asInstanceOf[Any]
您需要的是使用清单的具体化泛型

def cast[A <: AnyRef : Manifest](a : Any) : A 
  = manifest[A].erasure.cast(a).asInstanceOf[A]
我的建议是不要使用嵌套反射来确定目标是否真的是一个
列表[Int]
:这通常是不可行的。以下内容返回的目的是什么

cast[List[Int]](List[AnyVal](1, 2))

考虑这个解决方案:

trait -->[A, B] {
  def ->(a: A): B
}

implicit val StringToInt = new -->[String, Int] {
  def ->(a: String): Int = a.toInt
}

implicit val DateToLong = new -->[java.util.Date, Long] {
  def ->(a: java.util.Date): Long = a.getTime
}

def cast[A,B](t:A)(implicit ev: A --> B):B= ev.->(t)
优点是:

  • 它是类型安全的-编译器将告诉您类型是否无法强制转换
  • 您可以通过提供适当的隐式来定义强制转换规则
  • 现在您可以这样使用它:

    scala>  cast(new java.util.Date())
    res9: Long = 1361195427192
    
    scala>  cast("123")
    res10: Int = 123
    
    编辑

    我花了一些时间编写了这个高级代码。首先让我演示如何使用它:

    scala>    "2012-01-24".as[java.util.Date]
    res8: java.util.Date = Tue Jan 24 00:00:00 CET 2012
    
    scala>    "2012".as[Int]
    res9: Int = 2012
    
    scala>    "2012.123".as[Double]
    res12: Double = 2012.123
    
    scala>    "2012".as[Object]   // this is not working, becouse I did not provide caster to Object
    <console>:17: error: could not find implicit value for parameter $greater: -->[String,Object]
    "2012".as[Object]
    ^
    
    您可以使用Miles Sabin的可键入:

    它在许多情况下都能处理擦除,但仅在特定情况下:

    scala> import shapeless._; import syntax.typeable._
    import shapeless._
    import syntax.typeable._
    
    scala> val x = List(1, 2, 3)
    x: List[Int] = List(1, 2, 3)
    
    scala> val y = x.cast[List[String]]
    y: Option[List[String]] = None
    
    要查看它处理的案例集,可以参考其来源:


    您能否提供更多关于为什么需要强制转换的上下文?在您的示例中,
    val x=List(1,2,3)
    编译器推断出
    List[Int]
    ,因此使用castToType似乎毫无意义。如果你能完全避免
    Any
    asInstanceOf
    ,那是最好的选择。是的,基本上我的DB/memcached接口返回一个Any,我有一个类型化类(请参阅)需要返回一个类型R。所以我需要做一些转换,让编译器满意,但是如果强制转换不正确,我希望它也能抛出。你的
    castToType
    方法难道不是一个奇特的身份函数吗?您的意思是参数的类型为
    List[Any]
    ?有趣的是,即使你把它做成一个
    列表[Any]
    ,它仍然不会抛出。如果你允许一点挑剔,你就是在演示转换,而不是强制转换。诚然,通过类型系统表达转换的能力是Scala(和Haskell)最漂亮的功能之一,但正是类型擦除的问题在实践中的许多情况下阻碍了此功能的使用:如果您丢失了详细的类型信息,只剩下一个
    AnyVal
    ,您还失去了根据详细类型选择转换的功能
    scala>    "2012-01-24".as[java.util.Date]
    res8: java.util.Date = Tue Jan 24 00:00:00 CET 2012
    
    scala>    "2012".as[Int]
    res9: Int = 2012
    
    scala>    "2012.123".as[Double]
    res12: Double = 2012.123
    
    scala>    "2012".as[Object]   // this is not working, becouse I did not provide caster to Object
    <console>:17: error: could not find implicit value for parameter $greater: -->[String,Object]
    "2012".as[Object]
    ^
    
    trait -->[A, B] {
      def ->(a: A): B
    }
    
    implicit val StringToInt = new -->[String, Int] {
      def ->(a: String): Int = a.toInt
    }
    
    implicit val StringToDate = new -->[String, java.util.Date] {
      def ->(a: String): java.util.Date = (new java.text.SimpleDateFormat("yyyy-MM-dd")).parse(a)
    }
    
    implicit val StringToDouble = new -->[String, Double] {
      def ->(a: String): Double = a.toDouble
    }
    
    trait AsOps[A] {
      def as[B](implicit > : A --> B): B
    }
    
    implicit def asOps[A](a: A) = new AsOps[A] {
      def as[B](implicit > : A --> B) = > ->(a)
    }
    
    scala> import shapeless._; import syntax.typeable._
    import shapeless._
    import syntax.typeable._
    
    scala> val x = List(1, 2, 3)
    x: List[Int] = List(1, 2, 3)
    
    scala> val y = x.cast[List[String]]
    y: Option[List[String]] = None