Scala asInstanceOf with parameterized types

I would like to write a function that discards type A, where A can be, for example, a List [Int] or a more complex parameterized type, such as Map [Int, List [Int]].

def castToType[A](x: Any): A = {
  // throws if A is not the right type
  x.asInstanceOf[A]
}

Right now, due to the erasure of the type (I believe), the code is fun to work even if the type is incorrect. The error only appears when accessing, with 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

Is there a way to use manifests to work properly? Thank!

+26
source share
5 answers

Unfortunately, this is due to its inherent limitation asInstanceOf. I am really surprised to see that the skladok mentions this in more detail :

, 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
  }
}

:

scala> val x = List(1, 2, 3): Any
x: Any = List(1, 2, 3)

scala> failFastCast(List[String](), x)
java.lang.ArrayStoreException: java.lang.Integer
[...]

scala> failFastCast(List[Int](), x)
res22: List[Int] = List(1, 2, 3)

:

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

, A, , ...

+14

- , "" , List[Int] List[String], . , , A , Int String:

def cast[A](a : Any) = a.asInstanceOf[A]
//... is erased to
def erasedCast(a : Any) = a.asInstanceOf[Any]

, reified generics,

def cast[A <: AnyRef : Manifest](a : Any) : A 
  = manifest[A].erasure.cast(a).asInstanceOf[A]

AnyRef, , Class[_] (manifest.erasure), . :

scala> cast[String]("Hey")
res0: String = Hey

scala> cast[java.lang.Integer]("Hey")
  java.lang.ClassCastException
    at java.lang.Class.cast(Class.java:2990)
    at .cast(<console>:7)
    at .<init>(<console>:9)

scala> cast[List[String]](List("Hey"))
res2: List[String] = List(Hey)

scala> cast[List[Int]](List("Hey"))
res3: List[Int] = List(Hey)

- , , List[Int]: , , . ?

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

shapeless 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

, , :

https://github.com/milessabin/shapeless/blob/master/core/src/main/scala/shapeless/typeable.scala

+3

, - .

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])
+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

I spent some time and wrote this advanced code. First let me show how to use it:

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]
^

Pretty nice? See the magic of scala:

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)
}
+2
source

All Articles