在Scala中asInstanceOf 和(o:T)有什么区别?
我看到有两种方法在Scala中投射一个对象:
foo.asInstanceOf[Bar] (foo: Bar)
当我尝试,我发现asInstanceOf
不使用隐式转换,而另一个。
这两种方法之间有什么不同? 在哪里推荐使用一个呢?
-
foo.asInstanceOf[Bar]
是一个types转换,主要是一个运行时操作。 它说,应该强制编译器认为foo
是一个Bar
。 这可能会导致一个错误(一个ClassCastException
),如果当在运行时foo
被评估为除了一个Bar
之外的东西。 -
foo:Bar
是一个types归属 ,它完全是编译时的操作。 这是让编译器帮助理解你的代码的含义,而不是强迫它相信任何可能是不真实的; 使用types归属不会导致运行时失败。
types归属也可以用来触发隐式转换。 例如,你可以定义下面的隐式转换:
implicit def foo(s:String):Int = s.length
然后确保它的使用像这样:
scala> "hi":Int res29: Int = 2
将Int
types赋值给String
通常是一个编译时types错误,但在放弃编译器之前,将search可用的隐式转换以使问题消失。 将在编译时知道将在给定上下文中使用的特定隐式转换。
不用说,运行时错误是不可取的,所以你可以以types安全的方式(不使用asInstanceof
)指定事物的程度越好越好! 如果您发现自己使用asInstanceOf
,则应该使用match
来代替。
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 }
正如你所看到的types归属可以用来解决歧义。 有时它们可能是编译器无法parsing的(请参阅后面的内容),它将报告一个错误,您必须解决它。 在其他情况下(如在示例中),它只是使用“错误”的方法,而不是你想要的。 foo(a: String)
不会编译,表明types归属不是一个强制转换。 将它和上一行(编译器很高兴)相比较,但是你得到一个exception,所以错误被检测到,然后是types归属。
如果你也添加一个方法,你会得到一个无法解决的歧义
def foo(xs: Any*) { println("vararg") }
在这种情况下,foo的第一个和第三个调用将不会被编译,因为编译器无法决定是否要使用单个Any参数或可变参数调用foo,因为它们两个似乎都是同样好的。必须使用types归类来帮助编译器。
编辑另请参见Scala中types归属的目的是什么?
“Scala编程”在第15章 – “案例类和模式匹配”中详细介绍了这一点。
基本上,第二种forms可以在模式匹配中用作“types模式”,赋予isInstanceOf和asInstanceOffunction。 比较
if (x.isInstanceOf[String]) { val s = x.asInstanceOf[String] s.length } else ...
与
def checkFoo(x: Any) = x match { case s: String => s.length case m: Int => m case _ => 0 }
作者暗示,isInstance *做事方式的冗长是有意将您引入模式匹配风格的。
我不确定哪个模式对于没有testing的简单types转换更有效。
有一个差别的例子:
- types转换(asInstanceOf)是具有潜在运行时exception的运行时操作。
- 归属基本上只是在编译时进行的上传。
例:
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
我们也可以使用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"
我们可以定义隐式转换:
// 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)