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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3412068/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 02:20:27  来源:igfitidea点击:

What are the differences between asInstanceOf[T] and (o: T) in Scala?

scalacasting

提问by Mr_Qqn

I saw that there are two methods to cast an object in Scala:

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

foo.asInstanceOf[Bar]
(foo: Bar)

When I tried, I found that asInstanceOfdoesn't use implicit conversion whereas the other one does.

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

What are the differences of behavior between these two methods? And where is it recommended to use one over the other?

这两种方法在行为上有什么区别?在哪里推荐使用一个?

回答by Tom Crockett

  • foo.asInstanceOf[Bar]is a type cast, which is primarily a runtime operation. It says that the compiler should be coerced into believing that foois a Bar. This may result in an error (a ClassCastException) if and when foois evaluated to be something other than a Barat runtime.

  • foo:Baris a type ascription, which is entirely a compile-time operation. This is giving the compiler assistance in understanding the meaning of your code, without forcing it to believe anything that could possibly be untrue; no runtime failures can result from the use of type ascriptions.

  • foo.asInstanceOf[Bar]是一种类型的铸造,它主要是一个运行时操作。它说编译器应该被强制相信它foo是一个Bar. ClassCastException如果在运行时foo评估为 a 以外的值,则这可能会导致错误 (a ) Bar

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

Type ascriptions can also be used to trigger implicit conversions. For instance, you could define the following implicit conversion:

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

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

and then ensure its use like so:

然后确保它的使用如下:

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

Ascribing type Intto a Stringwould normally be a compile-time type error, but before giving up the compiler will search for available implicit conversions to make the problem go away. The particular implicit conversion that will be used in a given context is known at compile time.

将类型归因Int于 aString通常会导致编译时类型错误,但在放弃之前,编译器将搜索可用的隐式转换以使问题消失。将在给定上下文中使用的特定隐式转换在编译时已知。

Needless to say, runtime errors are undesirable, so the extent to which you can specify things in a type-safemanner (without using asInstanceof), the better! If you find yourself using asInstanceOf, you should probably be using matchinstead.

毋庸置疑,运行时错误是不可取的,因此您可以在多大程度上以类型安全的方式(不使用asInstanceof)指定事物就越好!如果您发现自己在使用asInstanceOf,那么您可能应该match改用。

回答by Sandor Murakozi

Pelotom's answer covers the theory pretty nice, here are some examples to make it clearer:

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
}

As you can see the type ascription can be used to resolve disambiguations. Sometimes they can be unresolvable by the compiler (see later), which will report an error and you must resolve it. In other cases (like in the example) it just uses the "wrong" method, not that you want. foo(a: String)does not compile, showing that the type ascription is not a cast. Compare it with the previous line, where the compiler is happy, but you get an exception, so the error is detected then with the type ascription.

如您所见,类型归属可用于解决歧义。有时它们可​​能无法被编译器解析(见下文),这将报告错误,您必须解决它。在其他情况下(例如在示例中),它只使用“错误”的方法,而不是您想要的。foo(a: String)不编译,表明类型归属不是强制转换。将它与前一行进行比较,编译器很高兴,但是您得到了一个异常,因此使用类型归属检测到错误。

You will get an unresolvable ambiguity if you also add a method

如果你还添加了一个方法,你会得到一个无法解决的歧义

def foo(xs: Any*) {
  println("vararg")
}

In this case the first and third invocation of foo will not compile, as the compiler can not decide if you want to call the foo with a single Any param, or with the varargs, as both of them seems to be equally good => you must use a type ascription to help the compiler.

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

Editsee also What is the purpose of type ascription in Scala?

编辑另见Scala中类型归属的目的是什么?

回答by Jon McAuliffe

Programming in Scalacovers this in a bit of detail in Chapter 15 - Case Classes and Pattern Matching.

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

Basically the second form can be used as Typed Patternin a pattern match, giving the isInstanceOfand asInstanceOffunctionality. Compare

基本上,第二种形式可以用作模式匹配中的类型化模式,提供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
}

The authors hint that the verbosity of the isInstance*way of doing things is intentional to nudge you into the pattern matching style.

作者暗示,isInstance*做事方式的冗长是有意将你推入模式匹配风格。

I'm not sure which pattern is more effective for a simple type cast without a test though.

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

回答by Luká? Jane?ek

There is example of the difference:

有一个区别的例子:

  1. Type cast (asInstanceOf) is a run-time operation with potentially run-time exception.
  2. Ascriptionis basically just an up-cast performed at compile-time.
  1. 类型转换 (asInstanceOf) 是具有潜在运行时异常的运行时操作。
  2. Ascription基本上只是在编译时执行的向上转换。

Example:

例子:

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

We can also call method using multiple-dispatch:

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

We can define implicit conversion:

我们可以定义隐式转换:

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