Scala编程-第6章 函数式对象- 高飞网

第6章 函数式对象

2017-03-18 11:14:34.0

    本章的重点在于定义函数式对象,也即,不具有任何可改变状态的对象的类。

    不可变对象的权衡

    不可变对象提供了若干强于可变的优点

    · 首先,不可变对象常常比可变对象更易理清头绪,因为它们没有会随着时间变化的复杂的状态空间
    · 其次,你可以很自由地传递不可变对象,但对于可变对象来说,传递给其他代码之前,需要先建造一个以防万一的副本。
    · 第三,一旦不可变对象完成构造之后,就不会有线程因为并发访问而破坏对象内部状态,因为根据没有线程可改变不可变对象的状态
    · 第四,不可变对象让哈希表键值更安全。比方说,如果可变对象在进入HashSet之后被改变,那么你下一次查找这个HashSet时就找不到这个对象了。

    不可变对象唯一的缺点

    有时需要复制很大的对象表而可变对象的更新可以在原址发生。有些情况下这会变得难以快速完成而可能产生性能瓶颈。


6.2 创建Rational

    rational number,即有理数,这里以有理数为例介绍函数式对象。

    Scala编译器将把类内部的任何既不是字段也不是方法定义的代码编译至主构造器中,如下的打印语句

scala> class Rational(n:Int,d:Int){
     | println("Created "+n+"/"+d)
     | }
defined class Rational

scala> new Rational(1,2)
Created 1/2
res2: Rational = Rational@7d37f1c


6.3 重新实现toString方法

    默认情况下,类对象的toString继承至Object类,打印类名@十六进制数。

scala> class Rational(n:Int,d:Int){
     |     println("Created "+n+"/"+d)
     |     override def toString = n + "/" + d 
     | }
defined class Rational

scala> new Rational(1,3)
Created 1/3
res3: Rational = 1/3


6.4 检查先决条件

    面向对象的好处是,把数据封装在类的内部,以保证在生命周期中,数据是有效的。上面的例子中,可以给分母d传递0,是不正确的。在Scala中可以使用先决条件(precodition)说明d必须是非零值。

scala> class Rational(n:Int,d:Int){
     |     require(d!=0)
     |     println("Created "+n+"/"+d)
     |     override def toString = n + "/" + d 
     | }
defined class Rational

scala> new Rational(2,0)
java.lang.IllegalArgumentException: requirement failed
  at scala.Predef$.require(Predef.scala:264)
  ... 30 elided


6.5 添加字段

    定义add方法,即分母相乘做为分母,分子互乘分母后相加

scala> class Rational(n:Int,d:Int){
     |     require(d!=0)
     |     println("Created "+n+"/"+d)
     |     override def toString = n + "/" + d 
     |     def add(that:Rational)=new Rational(n*that.d + that.n*d,d*that.d)
     | }
<console>:16: error: value d is not a member of Rational
           def add(that:Rational)=new Rational(n*that.d + that.n*d,d*that.d)
                                                      ^
<console>:16: error: value n is not a member of Rational
           def add(that:Rational)=new Rational(n*that.d + that.n*d,d*that.d)
                                                               ^
<console>:16: error: value d is not a member of Rational
           def add(that:Rational)=new Rational(n*that.d + that.n*d,d*that.d)
                                                                          ^

    但不幸的是,由于属性是私有的,因此不能访问。需要添加字段以取得访问权限

scala> class Rational(n:Int,d:Int){
     |     require(d!=0)
     |     val numer:Int = n 
     |     val denom:Int = d 
     |     println("Created "+n+"/"+d)
     |     override def toString = n + "/" + d 
     |     def add(that:Rational)=
     |         new Rational(
     |             numer*that.denom + that.numer*denom,
     |             denom*that.denom)
     | }
defined class Rational

计算一下1/2+2/3=7/6:

scala> val oneHalf = new Rational(1,2)
Created 1/2
oneHalf: Rational = 1/2

scala> val twoThirds = new Rational(2,3)
Created 2/3
twoThirds: Rational = 2/3

scala> oneHalf add twoThirds
Created 7/6
res9: Rational = 7/6


6.6 自指向

    即java中的this,下面以lessThan和max方法为例

scala> class Rational(n:Int,d:Int){
     |     require(d!=0)
     |     val numer:Int = n 
     |     val denom:Int = d 
     |     println("Created "+n+"/"+d)
     |     override def toString = n + "/" + d 
     |     def add(that:Rational)=
     |         new Rational(
     |             numer*that.denom + that.numer*denom,
     |             denom*that.denom)
     |     def lessThan(that:Rational)=this.numer*that.denom < that.numer*this.denom
     |     def max(that:Rational)=if(this.lessThan(that)) that else this
     | }
defined class Rational

scala> val oneThird = new Rational(1,3)
Created 1/3
oneThird: Rational = 1/3

scala> val quarter = new Rational(1,4)
Created 1/4
quarter: Rational = 1/4

scala> oneThird lessThan quarter
res0: Boolean = false

scala> oneThird max quarter
res1: Rational = 1/3


6.7 辅助构造器

    有些时候一个类里需要多个构造器。Scala里主构造器之外的构造器被称为辅助构造器。比如分母为1的数,如5/1,可以只写为5。Scala的辅助构造器定义开始于def this(...)。函数主体几乎完全是主构造器的调用。

scala> class Rational(n:Int,d:Int){
     |     require(d!=0)
     |     val numer:Int = n 
     |     val denom:Int = d 
     |     println("Created "+n+"/"+d)
     |     def this(n:Int)=this(n,1)//辅助构造器
     |     override def toString = n + "/" + d 
     |     def add(that:Rational)=
     |         new Rational(
     |             numer*that.denom + that.numer*denom,
     |             denom*that.denom)
     |     def lessThan(that:Rational)=this.numer*that.denom < that.numer*this.denom
     |     def max(that:Rational)=if(this.lessThan(that)) that else this
     | }
defined class Rational

scala> val y = new Rational(3)
Created 3/1
y: Rational = 3/1



6.8 私有字段和方法

    分数约简化,比如输入66/42,应保存相等的11/7

scala> class Rational(n:Int,d:Int){
     |     require(d!=0)
     |     private val g = gcd(n.abs,d.abs)//最大公约数
     |     val numer:Int = n/g 
     |     val denom:Int = d/g 
     |     println("Created "+n+"/"+d)
     |     def this(n:Int)=this(n,1)//辅助构造器
     |     override def toString = numer + "/" + denom
     |     def add(that:Rational)=
     |         new Rational(
     |             numer*that.denom + that.numer*denom,
     |             denom*that.denom)
     |     def lessThan(that:Rational)=this.numer*that.denom < that.numer*this.denom
     |     def max(that:Rational)=if(this.lessThan(that)) that else this
     |     private def gcd(a:Int,b:Int):Int={
     |         if(b==0) a else gcd(b,a%b)
     |     }   
     | }
defined class Rational

scala> new Rational(66,42)
Created 66/42
res3: Rational = 11/7


10.9 定义操作符

    在Scala中,+、*都是合法的方法名,因此可以用+代替add,用*代表相乘。

scala> class Rational(n:Int,d:Int){
     |     require(d!=0)
     |     private val g = gcd(n.abs,d.abs)//最大公约数
     |     val numer:Int = n/g
     |     val denom:Int = d/g
     |     println("Created "+n+"/"+d)
     |     def this(n:Int)=this(n,1)//辅助构造器
     |     override def toString = numer + "/" + denom
     |     def +(that:Rational)=
     |         new Rational(
     |             numer*that.denom + that.numer*denom,
     |             denom*that.denom)
     |     def *(that:Rational)= new Rational(numer*that.numer,denom*that.denom)
     |     def lessThan(that:Rational)=this.numer*that.denom < that.numer*this.denom
     |     def max(that:Rational)=if(this.lessThan(that)) that else this
     |     private def gcd(a:Int,b:Int):Int={
     |         if(b==0) a else gcd(b,a%b)
     |     }
     | }
defined class Rational

scala> new Rational(1,2) + new Rational(1,3)
Created 1/2
Created 1/3
Created 5/6
res6: Rational = 5/6

scala> new Rational(1,2) * new Rational(1,3)
Created 1/2
Created 1/3
Created 1/6
res7: Rational = 1/6


6.10 Scala的标识符

    字母数字式、操作符

6.11 方法重载


scala> class Rational(n:Int,d:Int){
     |     require(d!=0)
     |     private val g = gcd(n.abs,d.abs)//最大公约数
     |     val numer:Int = n/g 
     |     val denom:Int = d/g 
     |     println("Created "+n+"/"+d)
     |     def this(n:Int)=this(n,1)//辅助构造器
     |     override def toString = numer + "/" + denom
     |     def +(that:Rational)=
     |         new Rational(
     |             numer*that.denom + that.numer*denom,
     |             denom*that.denom)
     |     def +(i:Int)=new Rational(numer+i*denom,denom)//重载与整数的加法
     |     def *(that:Rational)= new Rational(numer*that.numer,denom*that.denom)
     |     def *(i:Int)=new Rational(numer*i,denom)//重载与整数的乘法
     |     def lessThan(that:Rational)=this.numer*that.denom < that.numer*this.denom
     |     def max(that:Rational)=if(this.lessThan(that)) that else this
     |     private def gcd(a:Int,b:Int):Int={
     |         if(b==0) a else gcd(b,a%b)
     |     }   
     | }
defined class Rational

scala> new Rational(1,2)+1
Created 1/2
Created 3/2
res1: Rational = 3/2

scala> new Rational(1,2)*3
Created 1/2
Created 3/2
res2: Rational = 3/2


6.12 隐匿转换

    现在与整数的乘法可以写成 new Rational(1,2)*3,但不能写成3* new Rational(1,2)

    但Scala有另的方法解决这个问题:可以创建在需要的时候上,自动把整数转换为有理数的隐式转换。

    在解释器加入:

implicit def intToRational(x:Int) = new Rational(x)

则:

scala> 3* new Rational(1,2)
Created 3/1
Created 1/2
Created 3/2
res4: Rational = 3/2