What are the differences between asInstanceOf[T] and (o: T) in Scala?

ScalaCasting

Scala Problem Overview


I saw that there are two methods to cast an object in Scala:

foo.asInstanceOf[Bar]
(foo: Bar)

When I tried, I found that asInstanceOf doesn't use implicit conversion whereas the other one does.

What are the differences of behavior between these two methods? And where is it recommended to use one over the other?

Scala Solutions


Solution 1 - Scala

  • foo.asInstanceOf[Bar] is a type cast, which is primarily a runtime operation. It says that the compiler should be coerced into believing that foo is a Bar. This may result in an error (a ClassCastException) if and when foo is evaluated to be something other than a Bar at runtime.

  • foo:Bar is a type ascription, which is entirely a compile-time operation. This is giving the compiler assistance in understanding the meaning of your code, without forcing it to believe anything that could possibly be untrue; no runtime failures can result from the use of type ascriptions.

Type ascriptions can also be used to trigger implicit conversions. For instance, you could define the following implicit conversion:

implicit def foo(s:String):Int = s.length

and then ensure its use like so:

scala> "hi":Int                                 
res29: Int = 2

Ascribing type Int to a String would normally be a compile-time type error, but before giving up the compiler will search for available implicit conversions to make the problem go away. The particular implicit conversion that will be used in a given context is known at compile time.

Needless to say, runtime errors are undesirable, so the extent to which you can specify things in a type-safe manner (without using asInstanceof), the better! If you find yourself using asInstanceOf, you should probably be using match instead.

Solution 2 - Scala

Pelotom's answer covers the theory pretty nice, here are some examples to make it clearer:

def foo(x: Any) {
  println("any")
}

def foo(x: String) {
  println("string")
}


def main(args: Array[String]) {
  val a: Any = new Object
  val s = "string"
    
  foo(a)                       // any
  foo(s)                       // string
  foo(s: Any)                  // any
  foo(a.asInstanceOf[String])  // compiles, but ClassCastException during runtime
  foo(a: String)               // does not compile, type mismatch
}

As you can see the type ascription can be used to resolve disambiguations. Sometimes they can be unresolvable by the compiler (see later), which will report an error and you must resolve it. In other cases (like in the example) it just uses the "wrong" method, not that you want. foo(a: String) does not compile, showing that the type ascription is not a cast. Compare it with the previous line, where the compiler is happy, but you get an exception, so the error is detected then with the type ascription.

You will get an unresolvable ambiguity if you also add a method

def foo(xs: Any*) {
  println("vararg")
}

In this case the first and third invocation of foo will not compile, as the compiler can not decide if you want to call the foo with a single Any param, or with the varargs, as both of them seems to be equally good => you must use a type ascription to help the compiler.

Edit see also What is the purpose of type ascription in Scala?

Solution 3 - Scala

Programming in Scala covers this in a bit of detail in Chapter 15 - Case Classes and Pattern Matching.

Basically the second form can be used as Typed Pattern in a pattern match, giving the isInstanceOf and asInstanceOf functionality. Compare

if (x.isInstanceOf[String]) {
  val s = x.asInstanceOf[String]
  s.length
} else ...

vs.

def checkFoo(x: Any) = x match {
  case s: String => s.length
  case m: Int => m
  case _ => 0
}

The authors hint that the verbosity of the isInstance* way of doing things is intentional to nudge you into the pattern matching style.

I'm not sure which pattern is more effective for a simple type cast without a test though.

Solution 4 - Scala

There is example of the difference:

  1. Type cast (asInstanceOf) is a run-time operation with potentially run-time exception.
  2. Ascription is basically just an up-cast performed at compile-time.

Example:

class Parent() { def method() {} }
class Child1 extends Parent() { def method1() {} }
class Child2 extends Parent() { def method2() {} }

// we return Parent type
def getChild1() : Parent = new Child1()
def getChild2() : Parent = new Child2()
def getChild()  : Child1 = new Child1()

(getChild1().asInstanceOf[Child1]).method1() // OK
(getChild1().asInstanceOf[Child2]).method2() // runtime ClassCastException

(getChild1() : Child2).method2() // compile-time error
(getChild2() : Child2).method2() // compile-time error
(getChild() : Parent).method1() // compile-time error
(getChild()).method()  // OK

// with asInstanceOf, we can cast to anything without compile-time error
getChild1().asInstanceOf[String] // runtime ClassCastException
getChild1().asInstanceOf[Int] // runtime ClassCastException

We can also call method using multiple-dispatch:

def prt(p: Parent) = println("parent")
def prt(ch: Child1) = println("child")

prt(new Parent()) // prints "parent"
prt((new Child1()) : Parent) // prints "parent"
prt(new Child1()) // prints "child"

prt(new Parent().asInstanceOf[Child1]) // runtime ClassCastException
prt(new Child1().asInstanceOf[Parent]) // prints "parent"

We can define implicit conversion:

// after definition of implicit conversions
implicit def toChild1(p: Parent) : Child1 = new Child1()
implicit def toChild2(p: Parent) : Child2 = new Child2()

(getChild1() : Child2).method2() // OK - implicit conversion to Child2 in ascription
(getChild2() : Child2).method2() // OK - implicit conversion to Child2 in ascription
(getChild2()).method1() // OK - implicit conversion to Child1 when calling method1()
(getChild2()).method2() // OK - implicit conversion to Child2 when calling method2()
(getChild2() : Parent).method() // OK - no implicit conversion
(getChild() : Parent).method1() // OK - implicit conversion to Child1 when calling method()

getChild1().asInstanceOf[Int] // still runtime ClassCastException (no implicit conversion)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMr_QqnView Question on Stackoverflow
Solution 1 - ScalaTom CrockettView Answer on Stackoverflow
Solution 2 - ScalaSandor MurakoziView Answer on Stackoverflow
Solution 3 - ScalaJon McAuliffeView Answer on Stackoverflow
Solution 4 - ScalaLukáš JanečekView Answer on Stackoverflow