0

将 String 强制转换为类型Any时,将不再自动将其视为String- 为什么?请参见以下示例:

val str = "foo"
val strAsAny = "bar".asInstanceOf[Any]
def f1(x: String) = println(x.toString)
def f2(x: Any) = println(x.toString)

f1(str) // works, type exactly given
f2(str) // works, subtype of Any given

f1(strAsAny) // works not, but strAsAny.isInstanceOf[String] = true !
f2(strAsAny) // works, type exactly given

这让我有点困惑,因为这两个值仍然具有 type String

scala> str.isInstanceOf[String]
res4: Boolean = true

scala> strAsAny.isInstanceOf[String]
res5: Boolean = true

我的猜测是,只有“向上”的 interhitance-chain 的类型转换才会自动完成。它是否正确 ?

4

2 回答 2

5

类型指定了您的期望,而不是您实际得到的。您可以将任何满足您期望的东西放入该插槽中。

有了Any,您期望的很少,所以任何事情都有效。使用String, 你期望length,charAt等等。所以 aString可以满足 an 的要求,Any但反之则不行。如果您碰巧将 aString放入一个Any插槽,那么您现在只承诺做Any可以做的事情,因此您不能将该值赋予期望 a 的东西String

这称为Liskov 替换原则,是(安全)面向对象设计的基本原则。

于 2012-07-04T18:54:51.170 回答
3

您将静态类型动态类型混为一谈。

静态类型是编译器知道的,并且在编译时就存在。

也就是说,当你这样做时:

val strAsAny = "bar".asInstanceOf[Any]

然后编译器将知道(或认为)strAsAny具有 type Any。它不会知道里面有String存储。

请注意,尽可能避免asInstanceOf,因为它的大多数有效用法都可以替换为类型归属 ( "bar" : Any) 或模式匹配 ( case anyAsStr: String => "got a String"),两者都是安全的。操作符asInstanceOf不安全的,因为它告诉编译器忽略它所知道的,并相信你告诉它的,即使它知道它是错误的。

因此,asInstanceOf它主要是一个编译时操作符,尽管它也会生成一些代码供 JVM 执行。

现在,编译器认为什么很重要,因为它只让你调用它知道可以调用的方法。因此,即使在运行时,strAsAny将包含 a String,编译器也不知道. 你告诉它忘记这"bar"是 a String,它就这样做了。因此,您不能在其上调用String方法。

另一方面,动态类型在运行时是真实的。在大多数情况下,对于像 Scala 这样的语言,编译器无法在运行时知道什么是正确的或不正确的。

而编译器是否知道某些东西,isInstanceOf是一个运行时操作。编译器对将会发生的事情没有任何可说的。

所以,当你这样做时:

strAsAny.isInstanceOf[String]

The compiler doesn't care -- it just compiles the code. When you execute the code, then the JVM will verify what kind of value is stored inside strAsAny, and verify that it is, indeed, a String, and then return true.

于 2012-07-04T21:15:08.090 回答