Scala的私有字段和定义操作符

私有字段和方法

上一个版本的Rational类里,我们只是分别用n初始化了numer,用d初始化了denom。结果,Rational的分子和分母可能比它所需要的要大。例如分数66/42,可以更约简化为相同的最简形式,11/7,但Rational的主构造器当前并不做这个工作:

    • scala> new Rational( 66 , 42 )  
    • res15: Rational = 66 / 42

要想对分数进行约简化,需要把分子和分母都除以最大公约数:greatest common divisor 。如:66和42的最大公约数是6。(另一种说法就是,6是能够除尽66和42的最大的整数。)66/42 的分子和分母都除以6就产生它的最简形式,11/7 。代码6.3展示了如何做到这点:

 
 
  1. class Rational(n: Int, d: Int) {  
  2.  require(d != 0 )  
  3.   private val g = gcd(n.abs, d.abs)  
  4.  val numer = n / g  
  5.  val denom = d / g  
  6.  def this (n: Int) = this (n, 1
  7. def add(that: Rational): Rational =   new Rational(  
  8.    numer * that.denom + that.numer * denom,  
  9.    denom * that.denom  
  10.   )  
  11.  override def toString = numer+ "/" +denom  
  12.   private def gcd(a: Int, b: Int): Int =  
  13.    if (b == 0 ) a  else  gcd(b, a % b)  
  14. }  

代码 6.3 带私有字段和方法的Rational

这个版本的Rational里,我们添加了私有字段,g,并修改了numer和denom的初始化器(初始化器:initializer是初始化变 量,例如初始化numer的“n / g”,的代码)。因为g是私有的,它只能在类的主体之内,而不能在外部被访问。我们还添加了一个私有方法,gcd,用来计算传入的两个Int的最大公约 数。比方说,gcd(12, 8)是4。正如你在4.1节中看到的,想让一个字段或方法私有化你只要把private关键字放在定义的前面。私有的“助手方法”gcd的目的是把类的其 它部分,这里是主构造器,需要的代码分离出来。为了确保g始终是正的,我们传入n和d的绝对值,调用abs即可获得任意整数的绝对值。

Scala编译器将把Rational的三个字段的初始化代码依照它们在源代码中出现的次序放入主构造器。所以g的初始化代 码,gcd(n.abs, d.abs),将在另外两个之前执行,因为它在源文件中出现得最早。g将被初始化为类参数,n和d,的绝对值的最大公约数。然后再被用于numer和 denom的初始化。通过把n和d整除它们的最大公约数,g,每个Rational都将被构造成它的最简形式:

 
 
  1. scala>  new Rational( 66 , 42 )  
  2. res24: Rational = 11 / 7

定义操作符
Rational加法的当前实现仅就完成功能来讲是没问题的,但它可以做得更好用。你或许会问你自己为什么对于整数或浮点数你可以写成:
  1. x + y 

但是如果是分数就必须写成:

  1. x.add(y) 

或至少是:

  1. x add y 

没有合理的解释为什么就必须是这样的。分数和别的数应该是一样的。数学的角度上看他们甚至比,唔,浮点数,更自然。为什么就不能使用自然的数学操作符呢?Scala里面你做得到。本章后续部分,我们会告诉你怎么做。

第一步是用通常的数学的符号替换add方法。这可以直接做到,因为Scala里+是合法的标识符。我们可以用+定义方法名。既然已经到这儿了,你可以同样实现一个*方法以实现乘法,结果展示在代码6.4中:

  1. class  Rational(n: Int, d: Int) {  
  2.  require(d !=  0 )  
  3.   private  val g = gcd(n.abs, d.abs)  
  4.  val numer = n / g  
  5.  val denom = d / g  
  6.  def  this (n: Int) =  this (n,  1 )  
  7.  def +(that: Rational): Rational =  
  8.    new  Rational(  
  9.    numer * that.denom + that.numer * denom,  
  10.    denom * that.denom  
  11.   )  
  12.  def *(that: Rational): Rational =  
  13.    new  Rational(numer * that.numer, denom * that.denom)  
  14.  override def toString = numer+ "/" +denom  
  15.   private  def gcd(a: Int, b: Int): Int =  
  16.    if  (b ==  0 ) a  else  gcd(b, a % b)  
  17. }  

代码 6.4 带操作符方法的Rational

有了这种方式定义的Rational类,你现在可以这么写了:

  1. scala> val x =  new  Rational( 12 )  
  2. x: Rational =  1 / 2  
  3. scala> val y =  new  Rational( 23 )  
  4. y: Rational =  2 / 3  
  5. scala> x + y  
  6. res32: Rational =  7 / 6  

与以往一样,在最后输入的那行里的语法格式相等于一个方法调用。你也能这么写:

  1. scala> x.+(y)  
  2. res33: Rational =  7 / 6  

不过这样写可读性不佳。

另外一件要提的是基于5.8节中提到的Scala的操作符优先级规则,Rational里面的*方法要比+方法绑定得更结实。或者 说,Rational涉及到+和*操作的表达式会按照预期的方式那样表现。例如,x + x * y会当作x + (x * y)而不是(x + x) * y:

  1. scala> x + x * y  
  2. res34: Rational =  5 / 6  
  3. scala> (x + x) * y  
  4. res35: Rational =  2 / 3  
  5. scala> x + (x * y)  
  6. res36: Rational =  5 / 6


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值