Product、case类和元组
case 关键字不仅可以推断出val,同时自动增加一些方法,那么增加了那些方法呢?
你定义的case 类会混入scala.Product 特征,它提供了几个关于实例字段的通用方法。例如,对于Person 的实例:
package cn.com.tengen.test.obj
case class Person(name: String,age: Option[Int] = None)
object Person extends App{
val p = Person("Lucky",Option(18))
println(p.productArity) //元素的个数 输出:2
println(p.productElement(0))//第1个元素 输出:Lucky
println(p.productElement(1))//第二个元素 输出:Some(18)
p.productIterator foreach println//便利 输出:Lucky Some(18)
}
Product 源码:
package scala
trait Product extends scala.Any with scala.Equals {
def productElement(n : scala.Int) : scala.Any
def productArity : scala.Int
def productIterator : scala.Iterator[scala.Any] = { /* compiled code */ }
def productPrefix : java.lang.String = { /* compiled code */ }
}
尽管以通用方法访问字段非常有用,但由于对各个字段使用的是Any 类型,而不是其具体类型,这种机制的作用受到了局限。
对于不同的字段数量, 也有Product 的子类型最大值为22。这些类型为特定的字段添加了一些方法,可以保持该字段的正确类型信息。例如:
Product2源码:
package scala
trait Product2[@scala.specialized +T1, @scala.specialized +T2] extends scala.Any with scala.Product {
override def productArity : scala.Int = { /* compiled code */ }
@scala.throws[scala.IndexOutOfBoundsException](classOf[scala.IndexOutOfBoundsException])
override def productElement(n : scala.Int) : scala.Any = { /* compiled code */ }
def _1 : T1
def _2 : T2
}
object Product2 extends scala.AnyRef {
def unapply[T1, T2](x : scala.Product2[T1, T2]) : scala.Option[scala.Product2[T1, T2]] = { /* compiled code */ }
}
这些方法返回了字段的真正类型。这里的类型参数是协变的,因为ProductN 特征只用于不可变的类型。用类似_1 的方法访问这些字段需要使用对应的类型参数T1,T1 处在协变的位置(即返回值类型)。
这些方法与用来访问元组元素的方法是相同的。事实上,所有的TupleN 类型都继承了对应的ProductN 特征,并提供了_1 到_N 方法的具体实现,N 最大可为22:
object Test {
def main(args: Array[String]): Unit = {
val t1 = ("Lucky", 26)
println(t1)//输出: (Lucky,26)
println(t1._1)//输出: Lucky
println(t1._2)//输出: 26
}
}
为什么个数的上限是22 ?这个数字的选择有些随意,但你可以合理地认为元组中有22 个元素无论如何都已经足够多了。
这对于人类来说确实如此,但不幸的是,存在一个常见的情景需要超出这个数量限制:保存大的数据“记录”中的字段(或列)。对于SQL 或NoSQL 数据集,包含超过22 个元素的情况并非罕见。元组很有用,至少对于小数据的确如此,因为元组可以保持字段(列)的顺序和类型。所以,22 个元素的限制是一个问题。
事实证明,在Scala 2.10 中,case 类也受到不超过22 个字段的限制。但这个实现上的限制在2.11 版本中取消了,所以数据应用可以为超过22 个元素的数据记录使用case 类。