我看到在Scala中有两种方法来转换对象:
foo.asInstanceOf[Bar]
(foo: Bar)
当我尝试时,我发现asInstanceOf
不使用隐式转换,而另一个则使用隐式转换。
这两种方法之间的行为有何不同?在哪里推荐使用其中一个?
答案 0 :(得分:81)
foo.asInstanceOf[Bar]
是强制转换类型,主要是运行时操作。它说应该强迫编译器相信foo
是Bar
。如果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,因为它们似乎同样好=>你必须使用类型归属来帮助编译器。
答案 2 :(得分:8)
Scala编程在第15章 - 案例类和模式匹配中详细介绍了这一点。
基本上,第二种形式可以在模式匹配中用作“类型模式”,提供isInstanceOf
和asInstanceOf
功能。比较
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)
有区别的例子:
示例:
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)