为什么一个'Any`铸造的`String`对象不再被认为是String(而另一种方式是它)?

时间:2012-07-04 18:48:04

标签: scala types

将字符串转换为类型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

这让我感到困惑,因为这两个值仍然具有类型String

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

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

我的猜测是,只有类型转换“向上”的interhitance-chain才会自动完成。这是对的吗?

2 个答案:

答案 0 :(得分:5)

类型指定您期望的内容,而不是您实际获得的内容。您可以将满足您期望的任何内容放入该位置。

使用Any,您的期望很少,所以任何工作都有效。使用String,您需要lengthcharAt等。因此,String可以满足Any的要求,但反之则不然。如果您恰好将String放入Any广告位,那么您现在只承诺执行Any可以执行的操作,因此您无法获取该值并将其投放到预期的内容中String

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

答案 1 :(得分:3)

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

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

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

val strAsAny = "bar".asInstanceOf[Any]

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

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

因此,asInstanceOf主要是编译时运算符,但它也会生成一些代码供JVM执行。

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

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

无论编译器是否知道某些内容,isInstanceOf都是运行时操作。编译器没有任何关于会发生什么的说法。

所以,当你这样做时:

strAsAny.isInstanceOf[String]

编译器无关紧要 - 它只是编译代码。当您执行代码时,JVM将验证strAsAny中存储的值的类型,并验证它确实是String,然后返回{ {1}}。