类型注释只是告诉编译器你期望从表达式中得到哪种类型,而这种类型必须是所有可能有效的类型中符合现有约束条件(如变量和类型声明)的一种,或者是表达式所应用的"是一个"类型之一,或者在作用域内存在适用的转换。
因此,java.lang.String extends java.lang.Object
,因此任何String
也是一个Object
。在您的示例中,您声明希望将表达式s
视为Object
而不是String
。由于没有阻止这种操作的约束条件,并且期望的类型是s
"是一个" 的类型之一,因此它可以正常工作。
那么,为什么要这样做呢?考虑以下情况:
scala> val s = "Dave"
s: java.lang.String = Dave
scala> val p = s: Object
p: java.lang.Object = Dave
scala> val ss = scala.collection.mutable.Set(s)
ss: scala.collection.mutable.Set[java.lang.String] = Set(Dave)
scala> val ps = scala.collection.mutable.Set(p)
ps: scala.collection.mutable.Set[java.lang.Object] = Set(Dave)
scala> ss += Nil
<console>:7: error: type mismatch;
found : scala.collection.immutable.Nil.type (with underlying type object Nil)
required: java.lang.String
ss += Nil
^
scala> ps += Nil
res3: ps.type = Set(List(), Dave)
你还可以通过在声明
ss
时添加类型为
Set[AnyRef]
的类型注释,或者使用类型为
s
的脚本来解决这个问题。
但是,类型声明只有在将值分配给标识符时才能实现相同的效果。当然,如果不介意在代码中添加一次性标识符,则始终可以这样做。例如,以下代码无法编译:
def prefixesOf(s: String) = s.foldLeft(Nil) {
case (head :: tail, char) => (head + char) :: head :: tail
case (lst, char) => char.toString :: lst
}
但是这个可以:
def prefixesOf(s: String) = s.foldLeft(Nil: List[String]) {
case (head :: tail, char) => (head + char) :: head :: tail
case (lst, char) => char.toString :: lst
}
在这里使用标识符代替 Nil
是愚蠢的。虽然我可以只写 List[String]()
,但并不总是可行的。例如:
def firstVowel(s: String) = s.foldLeft(None: Option[Char]) {
case (None, char) => if ("aeiou" contains char.toLower) Some(char) else None
case (vowel, _) => vowel
}
参考文献,这是Scala 2.7规范(2009年3月草案)对类型标注的说明:
Expr1 ::= ...
| PostfixExpr Ascription
Ascription ::= ‘:’ InfixType
| ‘:’ Annotation {Annotation}
| ‘:’ ‘_’ ‘*’
val x: Byte = 2
(说明:该段文字询问为什么不使用常见的语法形式来声明一个字节类型的变量并赋值为2。) - Jerry101