Why does `.asInstanceOf` sometimes throw, and sometimes not?

后端 未结 2 1363
南旧
南旧 2021-01-05 04:02

I was trying to answer this question, as I thought I knew the answer. Turns out, I did not know quite enough :/

Here is a test I have done:

class In         


        
相关标签:
2条回答
  • 2021-01-05 04:31

    This is because the class-cast-exception is only thrown when you do something with the value, call a method on it after the cast. In the REPL for example, you would have a toString call in the second case. Note:

    new Inst[String].as(3); ()           // nothing happens
    new Inst[String].as(3).toString; ()  // exception
    

    The reason why this takes the extra step is that Inst[T] is generic with type parameter T which is erased at runtime; only when the call-site (that has a static knowledge of type T) tries to call a method on the result, the actual type check occurs.


    For your follow-up question, toString is defined on any object and since T is generic you have a boxed integer (<: AnyRef) and toString and println succeed within the is method. So another example where the Try would fail is this:

    class Inst[T] { 
      def foo(x: Any)(implicit ev: T <:< String) = scala.util.Try {
        ev(as(x)).reverse
      }.isSuccess
    }
    
    new Inst[String].foo(3)  // false!
    
    0 讨论(0)
  • 2021-01-05 04:37

    While @0__'s answer explains why it doesn't work, here is how to make it work:

    class Inst[T](implicit tag: scala.reflect.ClassTag[T]) {
      def is(x: Any) = tag.runtimeClass.isInstance(x) 
      // scala.util.Try { as(x) }.isSuccess will work as well
      def as(x: Any): T = tag.runtimeClass.cast(x).asInstanceOf[T]
    }
    
    object Main extends App {
      println(new Inst[String].is(3))
      println(new Inst[String].as(3))
    }
    
    
    false
    java.lang.ClassCastException: Cannot cast java.lang.Integer to java.lang.String
        at java.lang.Class.cast(Class.java:3369)
    ...
    
    0 讨论(0)
提交回复
热议问题