一、函数式对象
1.1 rational类的规格和创建
- Rational类来源于有理数(rational number),来表示n(分子)/d(分母)的数字,同时对有理数的运算(加减乘除)建模,还具备归元化的特点。
下例子没有建模的:
scala> val oneHalf=new Rational(1,2) oneHalf: Rational = 1/2 scala> val twoThirds=new Rational(2,3) twoThirds: Rational = 2/3 scala> (oneHalf /7)+(1 twoThirds) <console>:13: error: value / is not a member of Rational (oneHalf /7)+(1 twoThirds) ^ <console>:13: error: value twoThirds is not a member of Int (oneHalf /7)+(1 twoThirds) scala> (oneHalf /7)+1 <console>:13: error: value / is not a member of Rational (oneHalf /7)+1
- Rational的创建,类名后的括号里的n、d两个类参数。Scala编译器会收集这两个类参数并创造出带同样的参数的主构造器。
Class Rational(n:Int,d:Int) scala> class Rational(n:Int,d:Int){ | println("Created"+n+"/"+d) | } defined class Rational scala> new Rational(1,2) Created1/2 res21: Rational = Rational@6fbfd6ed
默认情况下,Rational类继承了java.lang.Object类的toString实现,只会打印类名、@和十六进制数。
- 为了打印出具体的分子分母,可以通过重载Rational类的toString实现。
scala> class Rational(n:Int,d:Int){ | override def toString = n+"/"+d } defined class Rational scala> val x =new Rational(1,3) x: Rational = 1/3
1.2 rational先决条件
面向对象编程的优点之一是它允许你将数据封装在对象之内以确保数据在整个生命周期中的有效性。意味着必须确保rational类建有理数的有效性,
即分母不为0,故应该为主构造器定义一个先决的条件,说明d不为0,先决条件是对传递给方法或者构造器的值的限制,是调用者必须满足的需求。
- 一种方法是使用require方法,如:
scala> class Rational(n:Int,d:Int){ | require(d!=0) | override def toString = n+"/"+d | } defined class Rational scala> new Rational(2,0) java.lang.IllegalArgumentException: requirement failed at scala.Predef$.require(Predef.scala:268) ... 29 elided scala> new Rational(2,9) res13: Rational = 2/9
注:Require方法带来一个布尔型参数。如果传入的值为真,require将正常返回,反之,require将抛出IllegalArgumentException防止对象被构建。
1.3 添加字段
为了保持Rational的不可变性,add方法不能把传入的有理数加到自己身上,而是必须创建并返回全薪的Rational累加值。
class Rational(n:Int,d:Int){ require(d!=0) val number:Int=n val denom:Int=d override def toString =number + "/"+denom def add(that:Rational):Rational= new Rational( number*that.denom+that.number*denom, denom*that.denom ) } scala>val oneHalf=new Rational(1,2) oneHalf: Rational = 1/2
1.4 自指向this
关键字this指向当前执行方法被调用的对象实例,或者如果使用在构造器里的话,就是正被构建的对象实例。如下:
scala> class Rational(n:Int,d:Int){ | require(d!=0) | val number:Int=n | val denom:Int=d | override def toString =number + "/"+denom | def add(that:Rational):Rational= | new Rational( | number*that.denom+that.number*denom, | denom*that.denom | ) | def lessThan(that:Rational)=this.number*that.denom<that.number*this.denom | def max(that:Rational)=if(this.lessThan(that)) that else this | } defined class Rational scala> new Rational(1,2) res42: Rational = 1/2 scala> new Rational(1,4) res43: Rational = 1/4 scala> res43.lessThan(res42) res44: Boolean = true scala> res43.max(res42) res45: Rational = 1/2
1.5 辅助构造器
- Scala里主构造器之外的构造器被称为辅助构造器。作用:对5/1,能够写成Rational(5),这就需要给Rational添加辅助构造器预先设置分母为1
class Rational(n:Int,d:Int){ require(d!=0) val number:Int=n val denom:Int=d def this(n:Int) =this(n,1)//辅助构造器 override def toString =number + "/"+denom def add(that:Rational):Rational= new Rational( number*that.denom+that.number*denom, denom*that.denom ) }
1.6 私有字段和方法
- 为了约简化(归元化)为无差别的最简形式,即(66/42---->11/7),引入私有的最大公约数的字段和方法。
class Rational(n:Int,d:Int){ require(d!=0) private val g =gcd(n.abs,d.abs) val number=n/g val denom=d/g def this(n:Int) =this(n,1) def add(that:Rational):Rational= new Rational( number*that.denom+that.number*denom, denom*that.denom ) override def toString =number + "/"+denom private def gcd(a:Int,b:Int):Int = if(b==0) a else gcd(b,a%b) } scala> new Rational(99,66) res23: Rational = 3/2
17. 定义操作符
在Scala中用常用的数学符号+替换add方法,因为Scala里+是合法的标识符,能够用+定义方法名。
class Rational(n:Int,d:Int){ require(d!=0) private val g =gcd(n.abs,d.abs) val number=n/g val denom=d/g def this(n:Int) =this(n,1) def +(that:Rational):Rational= new Rational( number*that.denom+that.number*denom, denom*that.denom ) override def toString =number + "/"+denom private def gcd(a:Int,b:Int):Int = if(b==0) a else gcd(b,a%b) }
结果如下:
scala> new Rational(4,9) res30: Rational = 4/9 scala> new Rational(4,8) res31: Rational = 1/2 scala> res30+res31 res32: Rational = 17/18
1.8. 方法重载
在上面定义的都是有理数的运算,引入整数和有理数之间的运算,使用方法重载。
class Rational(n:Int,d:Int){ require(d!=0) private val g =gcd(n.abs,d.abs) val number=n/g val denom=d/g def this(n:Int) =this(n,1) def +(that:Rational):Rational= new Rational( number*that.denom+that.number*denom, denom*that.denom ) def +(i:Int):Rational= new Rational( number+i*denom,denom ) override def toString =number + "/"+denom private def gcd(a:Int,b:Int):Int = if(b==0) a else gcd(b,a%b) }
结果如下:
scala> new Rational(1,2) res37: Rational = 1/2 scala> 2 res38: Int = 2 scala> res37+res38 res39: Rational = 5/2