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

Mr_*_*Qqn 47 casting scala

我看到在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.

  • `foo:Bar`被称为_type ascription_.使用这个术语会很好,因为它可以更容易地找到有关它的更多信息.或者在搜索时找到这个答案.:-) (8认同)
  • 需要类型注释的一种情况是,当您要为null指定类型时,例如使用Java API时. (2认同)

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中类型归属的目的是什么?


Jon*_*ffe 8

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

基本上,第二种形式可以在模式匹配中用作"类型模式",提供isInstanceOfasInstanceOf功能.相比

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*做事方式的冗长是故意让你进入模式匹配风格.

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