这一章主要是以定义和完善一个有理数类Rational为线索,分析和介绍有关类定义,构造函数,方法重写,变量定义和私有化,以及对操作符的定义等。

一、Rational类定义和构造函数

1、定义一个空类

  1. class Rational(n: Int, d: Int)

  如果一个class没有函数体时,可以不用写花括号,上面的代码是最简形式。圆括号中的n和d是类参数,Scala编译器会根据这两个类参数,为该类生成一个对应的包含两个参数的主构造函数。

  在文本编辑器中输入以上代码后,保存为Rational.scala类型,使用scalac命令进行编译

  1. scalac Rational.scala

  然后使用javap -private类名的方式查看编译后的class文件内容

  1. javap -private Rational

  反编译后的结果如下

  

2、主构造函数定义

  上面对Rational类的定义是最简单的形式。如果需要往主构造函数中增加逻辑,可以在类定义之后用花括号包含一些代码

  1. class Rational(n: Int, d: Int) {
  2. println("Created "+ n +"/"+ d)
  3. }

  编译后,使用jd.exe查看反编译的代码,这一段println语句被加载到了主构造函数中。

  

  使用这个类定义构造一个Rational对象,

  

3、辅助构造函数的定义

  有时候一个类除了主构造函数之外,还需要定义多个不同形式的辅助构造函数。上面定义的主构造函数中,需要传入分子和分母两个参数。接下来定义一个只接收一个参数的构造函数,如果传入一个参数,则分母取默认值1。

  1. class Rational(n: Int, d: Int) {
  2. require(d != 0)
  3. val numer: Int = n
  4. val denom: Int = d
  5. def this(n: Int) = this(n, 1)
  6. override def toString = numer + "/" + denom
  7. def add(that: Rational): Rational =
  8. new Rational(numer * that.denom + that.numer * denom, denom * that.denom)
  9. }

  在Scala中每一个辅助构造函数的定义中必须首先调用另外一个构造函数,不管是另外一个辅助构造函数,还是主构造函数。所以,任何一个构造函数,最终都会直接或间接的调用了主构造函数。

二、重写toString方法

  上面的代码中,new一个Rational对象后得到的返回值res0会调用其默认的toString方法。如果需要重写toString

  1. class Rational(n: Int, d: Int) {
  2. override def toString = n + "/" + d
  3. }

  反编译后如下

  

  初始化一个Rational对象,重写方法与Java中类似,需要在前面加一个override关键字。

  

  

三、设置类的先决条件

  有理数可以写成分子/分母的形式,需要保证分母不为0。在不加这个限制条件时,

  

  可以实现一个require方法来达到这个目的,require接收一个boolean类型的参数

  

  1. class Rational(n: Int, d: Int) {
  2. require(d != 0)
  3. override def toString = n + "/" + d
  4. }

  此时再次执行上面的new Rational(1, 0),就会出现如下报错提示

  

四、定义变量

  接下来为Rational类定义一个add方法,该方法可以接收另一个Rational类型的对象并计算两者的和,返回一个求和后的Rational对象。

  按照Java中的思想,定义如下

  1. class Rational(n: Int, d: Int) {
  2. require(d != 0)
  3. override def toString = n + "/" + d
  4. def add(that: Rational): Rational =
  5. new Rational(n * that.d + that.n * d, d * that.d)
  6. }

  但是上面这段代码,在编译的时候就会报错

  

  正确的定义如下,

  1. class Rational(n: Int, d: Int) {
  2. require(d != 0)
  3. val numer: Int = n
  4. val denom: Int = d
  5. override def toString = numer + "/" + denom
  6. def add(that: Rational): Rational =
  7. new Rational(numer * that.denom + that.numer * denom, denom * that.denom)
  8. }

  执行下面三行代码

  1. val oneHalf = new Rational(1, 2)
  2. val twoThirds = new Rational(2, 3)
  3. oneHalf add twoThirds

  结果如下

  

  也可以直接访问某个对象的变量值,

  1. val r = new Rational(1, 2)
  2. r.numer
  3. r.denom

  

五、私有变量和方法

  写成分子除以分母形式的有理数,可以根据分子和分母的最大公约数进行化简。为此,可以在Rational类中定义一个求两个整数最大公约数的方法,这个方法只在Rational内部调用,可以定义为private类型,防止外部调用。得到的最大公约数也定义为private类型。

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

六、this关键字

  接下来,如果需要为Rational对象增加一个lessThan方法用于比较两个Rational对象的大小,增加一个max方法,用于获取两个Rational对象中值最大的那个。可以使用如下代码

  1. def lessThan(that: Rational) =
  2. this.numer * that.denom < that.number * this.denom
  3. def max(that: Rational) =
  4. if (this.lessThan(that)) that else this

  代码中的this关键字,指向当前对象本身。

七、定义操作符

  上面定义了Rational类型的add方法用于求两个有理数之和,add方法的使用是a add b,其中a和b都是Rational类型的。如果a和b都是int或者double类型,求两者之和直接是a + b的形式,那么如何使Rational类型变量也支持+操作呢?

  在前面的博客中提到过a + b实际上是a.+(b)的形式,如果把add方法直接命名成+,如下

  1. class Rational(n: Int, d: Int) {
  2. require(d != 0)
  3. private val g = gcd(n.abs, d.abs)
  4. val numer: Int = n / g
  5. val denom: Int = d / g
  6. def this(n: Int) = this(n, 1)
  7. def + (that: Rational): Rational =
  8. new Rational(numer * that.denom + that.numer * denom, denom * that.denom)
  9. def * (that: Rational): Rational =
  10. new Rational(numer * that.numer, denom * that.denom)
  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. }

  调用+*方法

  1. val x = new Rational(1, 2)
  2. val y = new Rational(2, 3)
  3. x + y
  4. x * y

  结果如下:

  

八、方法重载

  有时候,需要定义多个方法名相同,但是参数类型或个数不相同的重载方法。比如上一步中的+*方法,都必须接收Rational类型的参数,如果想要传递一个Int型参数进行求和或求积运算,程序就会报错。为此,需要重新定义方法名为+*,但是接收参数为Int型的两个方法。

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

九、Implicit关键字隐式转换

  经过上面的定义之后,可以支持Rational类型变量乘以Int变量的操作了。

  1. val r = new Rational(1, 2)
  2. r * 2

  结果如下

  

  但是,如果反过来,输入2 * r是会报错的,

  

  这是由于2 * r实质上调用的是2的*方法,而对于Int类型的2来说,是不支持传入一个Rational类型变量做乘法的。

  如果需要支持这种用法,可以使用implict关键字加入如下一行代码

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

  上面这一行代码会告诉Scala编译器自动使用该方法处理Int型的变量,重新执行2 * r命令

  

Programming In Scala笔记-第六章、函数式对象的更多相关文章

  1. Programming In Scala笔记-第十七章、Scala中的集合类型

    本章主要介绍Scala中的集合类型,主要包括:Array, ListBuffer, Arraybuffer, Set, Map和Tuple. 一.序列 序列类型的对象中包含多个按顺序排列好的元素,可以 ...

  2. Programming In Scala笔记-第四章、类和对象

    类似于Java,Scala中也有类和对象的概念. 一.类.属性和方法 1.类 类是对一类事物的抽象,当一个类被定义后,就可以以该定义为模板,定义该类的一系列对象.比如说有以下一个模板 人类: 有姓名: ...

  3. Programming In Scala笔记-第十一章、Scala中的类继承关系

    本章主要从整体层面了解Scala中的类层级关系. 一.Scala的类层级 在Java中Object类是所有类的最终父类,其他所有类都直接或间接的继承了Object类.在Scala中所有类的最终父类为A ...

  4. Programming In Scala笔记-第七章、Scala中的控制结构

    所谓的内建控制结构是指编程语言中可以使用的一些代码控制语法,如Scala中的if, while, for, try, match, 以及函数调用等.需要注意的是,Scala几乎所有的内建控制结构都会返 ...

  5. Programming In Scala笔记-第五章、Scala中的变量类型和操作

    这一章的一些基础性的东西,主要包括Scala中的基本变量类型,以及相关的一些操作符. 一.简单类型 下表中列出Scala语言中的基本类型,以及其字节长度,其中Byte, Short, Int, Lon ...

  6. scala编程(六)——函数式对象

    Rational 的式样书 分数:rational number 是一种可以表达为比率 d n 的数字,这里的 n 和 d 是数字,其中 d 不能为零.n 被称作是分子:numerator,d 被称作 ...

  7. 2018-11-16 中文代码示例之Programming in Scala笔记第四五六章

    续前文: 中文代码示例之Programming in Scala学习笔记第二三章. 同样仅节选有意思的例程部分作演示之用. 源文档仍在: program-in-chinese/Programming_ ...

  8. 2018-11-27 中文代码示例之Programming in Scala笔记第七八章

    续前文: 中文代码示例之Programming in Scala学习笔记第二三章 中文代码示例之Programming in Scala笔记第四五六章. 同样仅节选有意思的例程部分作演示之用. 源文档 ...

  9. 2018-12-09 疑似bug_中文代码示例之Programming in Scala笔记第九十章

    续前文: 中文代码示例之Programming in Scala笔记第七八章 源文档库: program-in-chinese/Programming_in_Scala_study_notes_zh ...

随机推荐

  1. 如何在Shell读取文件并赋值

    sys_info=$(cat /usr/local/sysconfig.txt)var=`echo   $sys_info   |   awk   -F ', '   '{print   $0} '  ...

  2. 深入理解Redux

    前面的话 Redux是Flux思想的另一种实现方式.Flux是和React同时面世的.React用来替代jQuery,Flux用来替换Backbone.js等MVC框架.在MVC的世界里,React相 ...

  3. MyBatis基础学习笔记--自总结

    一.MyBatis和jdbc的区别 jdbc的过程包括: 1.加载数据库驱动. 2.建立数据库连接. 3.编写sql语句. 4.获取Statement:(Statement.PrepareStatem ...

  4. FTP方式发布webservice

    以前我发布webservice的步骤是:在  C:\inetpub\wwwroot\路径下发布webservice,然后再在IIS中添加网站并制定路径,这样每次发布了webservice后,需要把发布 ...

  5. java之设计模式工厂三兄弟之简单工厂模式

    [学习难度:★★☆☆☆,使用频率:★★★☆☆] 工厂模式是最常用的一类创建型设计模式,通常我们所说的工厂模式是指工厂方法模式,它也是使用频率最高的工厂模式.本章将要学习的简单工厂模式是工厂方法模式的& ...

  6. 八:Vue下的国际化处理

    p { margin-bottom: 0.25cm; line-height: 120% } 1:首先安装 Vue-i8n npm install vue-i18n --save 注:-save-de ...

  7. vue插件移动框

    npm i dragablemodel -S(安装插件) import dragablemodel from 'dragablemodel' Vue.use(loading) 模板(组件) <d ...

  8. [HNOI 2003]消防局的设立

    Description 2020年,人类在火星上建立了一个庞大的基地群,总共有n个基地.起初为了节约材料,人类只修建了n-1条道路来 连接这些基地,并且每两个基地都能够通过道路到达,所以所有的基地形成 ...

  9. [NOI 2001]炮兵阵地

    Description 司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队.一个N*M的地图由N行M列组成,地图的每一格可能是山地(用“H” 表示),也可能是平原(用“P”表示),如下图.在每一 ...

  10. [HAOI2007]上升序列

    Description 对于一个给定的S={a1,a2,a3,…,an},若有P={ax1,ax2,ax3,…,axm},满足(x1 < x2 < … < xm)且( ax1 < ...