返回首页 Scala 抽象成员

 初始化抽象 vals

抽象定义的 vals 在某些时候起到父类参数的角色,它们允许你在子类中提供最父类中省略的定义,这对于 Trait 来说尤其重要,因为 Trait 没有提供你可以传入参数的构造函数。因此为 Trait 提供参数支持通常是通过抽象 vals 来实现的。
例如我们之前例子中使用过的有理数类型,使用 Trait 方式定义如下:


trait RationalTrait{
    val numerArg:Int
    val denomArg:Int
}

我们之前使用的 Rational 类定义定义了两个参数 n,d 代表分子和分母。 这里我们使用 RationalTrait 定义了两个抽象 vals 值, 为了构造这个 Trait 的一个实例,你需要提供这些抽象成员的实现,这里可以使用匿名类实例的方法构造一个实例:


scala> val r= new RationalTrait {
     | val numerArg = 1
     | val denomArg = 2
     | }
r: RationalTrait = $anon$1@341f55dd

这种构造 RationalTrait 实例在形式上和之前的 new Rational(1,2) 有点想像,但还是有些细节上的差别-在表达式初始化的顺序上的差异: 当你使用如下代码:


new Rational(expr1,expr2)

其中的两个表达式 expr1,expr2 在初始化类 Rational 之前就计算好了,因此在初始化 Rational 时,这些表达式是可以用的。而对于 Trait 来说,情况却相反:


new RationalTrait{
    val numerArg = expr1
    val denomArg = expr2
}

计算表达式 expr1,expr2 是和处理化匿名类实例的过程中进行的,而匿名类处理化是在 RationalTrait 之后进行的,因此在初始化 RationalTrait 时,这两个值是不可用的(或者是这两个值是缺省值0),这对于 RationalTrait 定义来说,不是个什么问题,因为 RationalTrait 的初始化没有使用到 numerArg 和 denomArg , 但对于下面的 RationalTrait 定义就存在问题了:


trait RationalTrait{
    val numerArg :Int
    val denomArg :Int

    require(denomArg !=0)
    private val g = gcd(numerArg,denomArg)

    val numer = numerArg/g
    val denom = denomArg/g

    private def gcd(a:Int,b:Int):Int =
        if(b==0) a else gcd(b, a % b)

    override def toString = numer + "/" + denom
}

如果此时,你使用某些表达式来构造这 个Trait 的实例,就会出问题了:


scala> new RationalTrait {
     | val numerArg = x 
     | val denomArg  = 2 * x
     | }
java.lang.IllegalArgumentException: requirement failed
  at scala.Predef$.require(Predef.scala:207)
  at RationalTrait$class.$init$(<console>:11)
  ... 39 elided

scala> new RationalTrait {
     | val numerArg = 1
     | val denomArg  = 2
     | }
java.lang.IllegalArgumentException: requirement failed
  at scala.Predef$.require(Predef.scala:207)
  at RationalTrait$class.$init$(<console>:11)
  ... 39 elided

这是因为在执行 RationalTrait 的初始化代码时 denomArg 的值还是0,就抛出异常了。 由此你可以知道抽象 Val 值和类参数之间的不同,对于使用 Trait 的这个问题,Scala 提供了两个解决方案:预先初始化的域和 lazy vals。我们在下篇中介绍他们。

上一篇: Type成员 下一篇: 预先初始化成员的...