Scala中类型说明的目的是什么?

85

规范中并没有太多关于类型注释的信息,也没有关于其目的的任何内容。除了 "使可变参数传递工作" 之外,我还可以用类型注释做什么?下面是一些 Scala REPL,演示了使用类型注释的语法和效果。

scala> val s = "Dave"
s: java.lang.String = Dave

scala> val p = s:Object
p: java.lang.Object = Dave

scala> p.length
<console>:7: error: value length is not a member of java.lang.Object
       p.length
         ^
scala> p.getClass
res10: java.lang.Class[_ <: java.lang.Object] = class java.lang.String

scala> s.getClass
res11: java.lang.Class[_ <: java.lang.Object] = class java.lang.String

scala> p.asInstanceOf[String].length
res9: Int = 4
5个回答

91

类型注释只是告诉编译器你期望从表达式中得到哪种类型,而这种类型必须是所有可能有效的类型中符合现有约束条件(如变量和类型声明)的一种,或者是表达式所应用的"是一个"类型之一,或者在作用域内存在适用的转换。

因此,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}
             | ‘:’ ‘_’ ‘*’

28

有一种可能是当涉及到网络和串行协议级别的东西时,使用如下方法:

val x = 2 : Byte

比...更加简洁干净

val x = 2.asInstanceOf[Byte]
第二种形式也是运行时转换(不由编译器处理),可能会导致一些有趣的溢出 / 下溢情况。

1
嘿,学到了新东西。我做了相当多的网络协议工作。好知道! - Justin W
24
为什么不使用日常语法? val x: Byte = 2 (说明:该段文字询问为什么不使用常见的语法形式来声明一个字节类型的变量并赋值为2。) - Jerry101

0
类型推断:我们可以在源代码中跳过显式给出某个东西的类型名称,这被称为类型推断。(尽管在某些特殊情况下需要。)
类型注释:明确指定某个东西的类型被称为类型注释。它能带来什么不同?
例如:val x = 2 : Byte 另请参见: 1. 我们可以明确给出函数的返回类型。
def t1 : Option[Option[String]] = Some(None)

> t1: Option[Option[String]]

声明这个的另一种方式可能是:

def t2 = Some(None: Option[String])
> t2: Some[Option[String]]

这里我们没有明确给出Option [Option [String]] 的返回类型,编译器推断为Some [Option [String]] 。 为什么是Some [Option [String]] 呢?因为我们在定义中使用了类型说明。

  1. 另一种方式是:

    def t3 = Some(None)

    > t3: Some[None.type]

这次我们没有明确告诉编译器任何东西(也没有告诉它这个定义),但它推断出我们的定义为Some[None.type]。


0

我使用类型注释来弥补Scala类型推断中的漏洞。例如,对类型为A的集合进行foldLeft操作需要一个类型为B的初始元素和一个函数(B, A) => B,该函数用于将集合的元素折叠到初始元素中。类型B的实际值是从初始元素的类型推断出来的。由于Nil扩展了List[Nothing],将其作为初始元素会引起问题:

scala> val x = List(1,2,3,4)
x: List[Int] = List(1, 2, 3, 4)

scala> x.foldLeft(Nil)( (acc,elem) => elem::acc)
<console>:9: error: type mismatch;
 found   : List[Int]
 required: scala.collection.immutable.Nil.type
              x.foldLeft(Nil)( (acc,elem) => elem::acc)
                                                 ^

scala> x.foldLeft(Nil:List[Int])( (acc,elem) => elem::acc )
res2: List[Int] = List(4, 3, 2, 1)

或者,你可以直接使用List.empty[Int]代替Nil:List[Int]。

scala> x.foldLeft(List.empty[Int])( (acc,elem) => elem::acc )
res3: List[Int] = List(4, 3, 2, 1)

编辑:List.empty[A] 的实现方式为

override def empty[A]: List[A] = Nil

(来源)

这实际上是Nil:List[A]的更冗长形式


为什么不使用 x.foldLeft[List[Int]](Nil)( (acc,elem) => elem::acc ) - Jeffrey Aguilera

0

如果您愿意花点时间阅读,您可能会发现这个线程很有启发性,尽管有些复杂难懂。重要的是要注意,您正在向类型检查器添加约束提示-这使您对编译阶段的操作具有更多控制权。


我在这里重新发布了我的问题,以便它能够被保留;我选择的答案是那个帖子中对我来说最清晰的一个。 - davetron5000
抱歉,刚看到这条消息。已完成。 - Justin W

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接