Scala中asInstanceOf [T]和(o:T)之间有什么区别?

时间:2010-08-05 05:51:08

标签: scala casting

我看到在Scala中有两种方法来转换对象:

foo.asInstanceOf[Bar]
(foo: Bar)

当我尝试时,我发现asInstanceOf不使用隐式转换,而另一个则使用隐式转换。

这两种方法之间的行为有何不同?在哪里推荐使用其中一个?

4 个答案:

答案 0 :(得分:81)

  • foo.asInstanceOf[Bar]强制转换类型,主要是运行时操作。它说应该强迫编译器相信fooBar。如果ClassCastException在运行时被评估为foo以外的其他内容,则可能会导致错误(Bar

  • foo:Bar归属类型,完全是编译时操作。这使编译器能够帮助理解代码的含义,而不必强迫它相信任何可能不真实的东西;使用类型归属不会导致运行时失败。

类型描述也可用于触发隐式转换。例如,您可以定义以下隐式转换:

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

然后确保其使用如下:

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

Int类型归属为String通常是编译时类型错误,但在放弃编译器之前,将搜索可用的隐式转换以使问题消失。在编译时将知道将在给定上下文中使用的特定隐式转换。

毋庸置疑,运行时错误是不受欢迎的,因此您可以用类型安全方式指定内容的程度(不使用asInstanceof),效果更好!如果您发现自己使用asInstanceOf,则应该使用match代替。

答案 1 :(得分:16)

Pelotom的答案涵盖了理论上非常好的,这里有一些例子可以让它更清晰:

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
}

如您所见,类型归属可用于解决消歧。有时它们可​​能由编译器无法解析(请参阅后面的内容),它将报告错误并且您必须解决它。在其他情况下(如示例中),它只使用“错误”方法,而不是您想要的方法。 foo(a: String)无法编译,表明类型归属不是强制转换。将它与上一行比较,编译器很满意,但是你得到一个异常,所以检测到错误然后使用类型归属。

如果您还添加方法

,您将获得无法解决的歧义
def foo(xs: Any*) {
  println("vararg")
}

在这种情况下,foo的第一次和第三次调用将无法编译,因为编译器无法决定是否要使用单个Any参数或varargs调用foo,因为它们似乎同样好=>你必须使用类型归属来帮助编译器。

修改另请参阅What is the purpose of type ascription in Scala?

答案 2 :(得分:8)

Scala编程第15章 - 案例类和模式匹配中详细介绍了这一点。

基本上,第二种形式可以在模式匹配中用作“类型模式”,提供isInstanceOfasInstanceOf功能。比较

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
}

作者暗示isInstance*做事方式的冗长是故意让你进入模式匹配风格。

我不确定哪种模式对于没有测试的简单类型转换更有效。

答案 3 :(得分:2)

有区别的例子:

  1. 类型转换(asInstanceOf)是一个运行时操作,可能存在运行时异常。
  2. Ascription基本上只是在编译时执行的向上转换。
  3. 示例:

    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
    

    我们也可以使用multi-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"
    

    我们可以定义隐式转换:

    // 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)