Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.2k views
in Technique[技术] by (71.8m points)

scala - Why wrapping a generic method call with Option defers ClassCastException?

Lets say I have an array like this*:

val foo: Any = 1 : Int
Option(foo.asInstanceOf[String])

which fails for obvious reason:

// java.lang.ClassCastException: java.lang.Integer cannot be cast to 
// java.lang.String
// ... 48 elided

Next lets consider a following class:

case class DummyRow() {
  val foo: Any = 1 : Int
  def getAs[T] = foo.asInstanceOf[T]
  def getAsOption[T] = Option(foo.asInstanceOf[T])
}

As far as I can tell getAs should behave the same way as the previous apply followed by asInstanceOf.

Surprisingly it is not the case. When called alone it throws an exception:

DummyRow().getAs[String]
// java.lang.ClassCastException: java.lang.Integer cannot be cast to 
// java.lang.String
// ... 48 elided

but when wrapped with Option succeeds:

val stringOption = Option(DummyRow().getAs[String])
// Option[String] = Some(1)

DummyRow().getAsOption[String]
// Option[String] = Some(1)

and fails only when I try to access wrapped value:

stringOption.get
// java.lang.ClassCastException: java.lang.Integer cannot be cast to 
// java.lang.String
// ... 48 elided

So what happens here? It seems to be limited ClassCastException so I guess it is related to some ugly thing like type erasure.


* Any and asInstanceOf are there to mimic a behavior of the 3rd party code so please lets not dwell on that.

** Tested in Scala 2.10.5, 2.11.7

*** If you're interested in the context you can take a look at Using contains in scala - exception

**** Other relevant questions linked in the comments:

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Below is a simplified version of your problem with an additional case for Any

def getAs[T] = (1:Int).asInstanceOf[T]

//blows up
getAs[String]

//blows up
def p(s:String): Unit = {}
p(getAs[String])

//works
def p[T](s:T): Unit = {}
p(getAs[String])

//works
def p(s:Any): Unit = {}
p(getAs[String])

Because you create a method with a generic parameter, the runtime doesn't need to "touch" the value because it does not care. Generic will be treated as Any/Object at runtime.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...