Skip to content

Latest commit

 

History

History
262 lines (214 loc) · 6.74 KB

第六章 功能对象.md

File metadata and controls

262 lines (214 loc) · 6.74 KB

第六章 功能对象

以一个有理数类为例演示Scala面向对象编程的各个方面

6.1 有理数定义

设想中的功能:

val 一半 = new 有理数(1, 2) // 结果为1/2
val 三分之二 = new 有理数(2, 3)
(一半 / 7) + (1 - 三分之二) // 结果为有理数17/42

6.2 构造

class 有理数(分子: Int, 分母: Int)

不可变对象有好处, 也有短处. 这里创建不可变对象.

任何类内不为变量或者方法声明的部分都会被置于首要构建器.

class 有理数(分子: Int, 分母: Int) {
  println("已创建" + 分子 + "/" + 分母)
}

调用new 有理数(1, 2)会打印已创建1/2

6.3 重新实现toString方法

class 有理数(分子: Int, 分母: Int) {
  override def toString = 分子 + "/" + 分母
}

运行:

scala> new 有理数(1, 2)
res1: 有理数 = 1/2

6.4 检查先决条件

class 有理数(分子: Int, 分母: Int) {
  require(分母 != 0)
  override def toString = 分子 + "/" + 分母
}

6.5 加变量

class 有理数(分子: Int, 分母: Int) {
  require(分母 != 0)
  val 分子值: Int = 分子
  val 分母值: Int = 分母
  override def toString = 分子值 + "/" + 分母值
  def (: 有理数): 有理数 =
    new 有理数(
      分子值 * 数.分母值 + 数.分子值 * 分母值,
      分母值 * 数.分母值
    )
}

运行:

scala> val 一半 = new 有理数(1, 2)
一半: 有理数 = 1/2

scala> val 三分之二 = new 有理数(2, 3)
三分之二: 有理数 = 2/3

scala> 一半 加 三分之二
res3: 有理数 = 7/6

也可取对象内变量值:

scala> 一半.分子值
res4: Int = 1

scala> 一半.分母值
res5: Int = 2

6.6 自引用

def 小于(: 有理数) =
  this.分子值 * 数.分母 < 数.分子 * this.分母

def 最大(: 有理数) =
  if (this.小于(数)) 数 else this

6.7 辅助构建器

所有辅助构建器的第一句必须调用另一个构建器, 因此必须是this(...)格式

def this(: Int) = this(数, 1)

6.8 私有变量和方法

在有理数类中, 新添如下:

class 有理数(分子: Int, 分母: Int) {
  require(分母 != 0)
  private val 公约数 = 最大公约数(分子.abs, 分母.abs)

  val 分子值: Int = 分子 / 公约数
  val 分母值: Int = 分母 / 公约数
  override def toString = 分子值 + "/" + 分母值
  def (: 有理数): 有理数 =
    new 有理数(
      分子值 * 数.分母值 + 数.分子值 * 分母值,
      分母值 * 数.分母值
    )
  private def 最大公约数(: Int, : Int): Int =
    if (乙 == 0) 甲 else 最大公约数(乙, 甲 % 乙)
}

运行:

scala> new 有理数(66, 42)
res7: 有理数 = 11/7

6.9 定义运算符

定义了+和*:

class 有理数(分子: Int, 分母: Int) {
  require(分母 != 0)
  private val 公约数 = 最大公约数(分子.abs, 分母.abs)

  val 分子值: Int = 分子 / 公约数
  val 分母值: Int = 分母 / 公约数

  def this(: Int) = this(数, 1)

  def + (: 有理数): 有理数 =
    new 有理数(
      分子值 * 数.分母值 + 数.分子值 * 分母值,
      分母值 * 数.分母值
    )

  def * (: 有理数): 有理数 =
    new 有理数(分子值 * 数.分子值, 分母值 * 数.分母值)

  override def toString = 分子值 + "/" + 分母值

  private def 最大公约数(: Int, : Int): Int =
    if (乙 == 0) 甲 else 最大公约数(乙, 甲 % 乙)
}

运行. 运算符优先级见5.9:

val x = new 有理数(1, 2)
val y = new 有理数(2, 3)
x + y
x + x * y
(x + x) * y
x + (x * y)

6.10 Scala标识符

之前看到了两种标识符: 数字字母混合, 运算符

与Java不同的是, 常量在Java中是大写加下划线分隔, 如X_OFFSET; Scala只要求驼峰命名而且开头大写, 如XOffset

运算符标识符也有不同, x<-y在Java中被识别为x < - y, 但Scala中将<-解析为单个标识符

混合标识符, 如unary_+定义一元+运算符, myvar_=定义赋值运算符 (第18章更多)

字面量标识符如x, yield, <clinit>. 一个用法是访问Java的Thread类, 由于yield是Scala保留字, 不能写Thread.yield(), 但可以用反引号: Thread.`yield`()

6.11 方法重载

支持有理数+整数, 顺便加上減和除. 源码在有理数.scala(代码将命名作了调整, 以使得重复使用的命名尽量简约)

class 有理数(分子: Int, 分母: Int) {
  require(分母 != 0)
  private val 公约数 = 最大公约数(分子.abs, 分母.abs)

  val 分子值: Int = 分子 / 公约数
  val 分母值: Int = 分母 / 公约数

  def this(: Int) = this(数, 1)

  def + (: 有理数): 有理数 =
    new 有理数(
      分子值 * 数.分母值 + 数.分子值 * 分母值,
      分母值 * 数.分母值
    )

  def + (: Int): 有理数 =
    new 有理数(分子值 +* 分母值, 分母值)

  def - (: 有理数): 有理数 =
    new 有理数(
      分子值 * 数.分母值 - 数.分子值 * 分母值,
      分母值 * 数.分母值
    )

  def - (: Int): 有理数 =
    new 有理数(分子值 -* 分母值, 分母值)

  def * (: 有理数): 有理数 =
    new 有理数(分子值 * 数.分子值, 分母值 * 数.分母值)

  def * (: Int): 有理数 =
    new 有理数(分子值 * 数, 分母值)

  def / (: 有理数): 有理数 =
    new 有理数(分子值 * 数.分母值, 分母值 * 数.分子值)

  def / (: Int): 有理数 =
    new 有理数(分子值, 分母值 * 数)

  override def toString = 分子值 + "/" + 分母值
  
  private def 最大公约数(: Int, : Int): Int =
    if (乙 == 0) 甲 else 最大公约数(乙, 甲 % 乙)
}

运行:

scala> val x = new 有理数(2, 3)
x: 有理数 = 2/3

scala> x * x
res4: 有理数 = 4/9

scala> x * 2
res5: 有理数 = 4/3

6.12 隐式转换

现在仍不支持2*x:

scala> 2 * x
<console>:13: error: overloaded method value * with alternatives:
  (x: Double)Double <and>
  (x: Float)Float <and>
  (x: Long)Long <and>
  (x: Int)Int <and>
  (x: Char)Int <and>
  (x: Short)Int <and>
  (x: Byte)Int
 cannot be applied to (有理数)
       2 * x

运行. 注意如果在类内声明, 就只在类内有效, 而在交互环境内就无效, 因此必须在交互环境声明:

implicit def 整数到有理数(: Int) = new 有理数(数)

之后再运行成功. 更多隐式转换见21章

scala> val r = new 有理数(2, 3)
r: 有理数 = 2/3

scala> 2 * r
res0: 有理数 = 4/3

6.13 注意点

运算符方法和隐式转换过头会导致代码可读性下降, 比如当运算符过于复杂或者晦涩, 而隐式转换的问题是它可能不写在源代码中. 因此需谨慎使用.

(第六章完)