我看到在Scala中有两种方法来转换对象:
foo.asInstanceOf[Bar]
(foo: Bar)
Run Code Online (Sandbox Code Playgroud)
当我尝试时,我发现asInstanceOf不使用隐式转换,而另一个则使用隐式转换.
这两种方法之间的行为有何不同?在哪里建议使用一个而不是另一个?
Tom*_*ett 84
foo.asInstanceOf[Bar]是一种类型的铸造,它主要是一个运行时操作.它说编译器应该被强迫相信它foo是一个Bar.这可能导致错误(a ClassCastException)if和when foo被评估为Bar运行时以外的其他东西.
foo:Bar是一种类型归属,它完全是一个编译时操作.这使编译器能够帮助理解代码的含义,而不必强迫它相信任何可能不真实的东西; 使用类型归属不会导致运行时故障.
类型归属也可用于触发隐式转换.例如,您可以定义以下隐式转换:
implicit def foo(s:String):Int = s.length
Run Code Online (Sandbox Code Playgroud)
然后确保其使用如下:
scala> "hi":Int
res29: Int = 2
Run Code Online (Sandbox Code Playgroud)
将类型归类Int为a String通常是编译时类型错误,但在放弃编译器之前将搜索可用的隐式转换以使问题消失.将在编译时知道将在给定上下文中使用的特定隐式转换.
毋庸置疑,运行时错误是不可取的,因此您可以以类型安全的方式指定事物(不使用asInstanceof)的程度越大越好!如果你发现自己在使用asInstanceOf,你可能应该使用它match.
San*_*ozi 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
}
Run Code Online (Sandbox Code Playgroud)
如您所见,类型归属可用于解决消歧.有时它们可能由编译器无法解析(请参阅后面的内容),它将报告错误并且您必须解决它.在其他情况下(如示例中),它只使用"错误"方法,而不是您想要的方法.foo(a: String)不编译,表明类型归属不是强制转换.将它与上一行比较,编译器很满意,但是你得到一个异常,所以检测到错误然后使用类型ascription.
如果您还添加方法,您将获得无法解决的歧义
def foo(xs: Any*) {
println("vararg")
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,foo的第一次和第三次调用将无法编译,因为编译器无法决定是否要使用单个Any参数或varargs调用foo,因为它们两者似乎同样好=>你必须使用类型归属来帮助编译器.
编辑请参阅Scala中类型归属的目的是什么?
Scala中的编程在第15章 - 案例类和模式匹配中详细介绍了这一点.
基本上,第二种形式可以在模式匹配中用作"类型模式",提供isInstanceOf和asInstanceOf功能.相比
if (x.isInstanceOf[String]) {
val s = x.asInstanceOf[String]
s.length
} else ...
Run Code Online (Sandbox Code Playgroud)
与
def checkFoo(x: Any) = x match {
case s: String => s.length
case m: Int => m
case _ => 0
}
Run Code Online (Sandbox Code Playgroud)
作者暗示,isInstance*做事方式的冗长是故意让你进入模式匹配风格.
我不确定哪种模式对于没有测试的简单类型转换更有效.