实践操作定义Rational类
Rational类的定义规范
有理数rational定义:一个有理数rational可以表示为分数形式 n/d,其中n是分子(numerator),d是分母(denominator)
定义Rational
class Rational(n:Int, d:Int)
- scala类的定义可以有参数,scala编译器会将不属于类成员和类方法的其它代码用作类的主构造函数
- 创建Rational对象时,自动执行类定义的代码
scala> class Rational (n:Int, d:Int){
| println(“Created” + n + “/” + d)
| }
defined class Rational
scala> new Rational(1,2)
Created1/2
res14: Rational = Rational@5b7ee56c
重新定义类的toString方法
上面定义最后打印出Rational@5b7ee56c,是因为使用了default的类的toString()定义。default的实现是打印出对象的类名称+@+16进制数(对象的地址)
scala中可以使用override来重载基类定义的方法,必须使用override关键字表示重新定义的基类中的成员。比如:
scala> class Rational (n:Int,d:Int){
| override def toString = n+"/"+ d
| }
defined class Rational
scala> new Rational(1,3)
res15: Rational = 1/3
前提条件检查
给Rational设置前提条件 d不等于0
Scala 中解决这个问题的一个方法是使用 require 方法(require 方法为 Predef 对象定义的一个方法,Scala 环境自动载入这个类的定义,因此无需使用 import 引入这个对象)
scala> class Rational (n:Int,d:Int){
| require(d!=0)
| override def toString = n+"/"+d
| }
defined class Rational
scala> new Rational (5,0)
java.lang.IllegalArgumentException: requirement failed
添加成员变量
实现两个Rational对象相加的操作,加法返回一个新的Rational对象
class Rational (n:Int, d:Int) {
require(d!=0)
val number =n
val denom =d
override def toString = number + "/" +denom
def add(that:Rational) =
new Rational(
number * that.denom + that.number* denom,
denom * that.denom
)
}
类参数的访问仅限于定义类的方法本身,对于that来说,无法使用that.d来访问类参数,因此需要把d应以为类的成员变量
number 和 denom 以及 add 都可以不定义类型,Scala 编译器能够根据上下文推算出它们的类型。
自身引用
一般来说,引用类成员函数无需使用this,但是引用对象本身,this 就无法忽略
构造辅助函数
def this(n:Int) = this(n,1)
- scala定义辅助构造函数使用this(…)语法,所有的辅助构造函数的名称为this.()
私有成员变量和方法
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
override def toString = number + "/" +denom
def add(that:Rational) =
new Rational(
number * that.denom + that.number* denom,
denom * that.denom
)
def this(n:Int) = this(n,1)
private def gcd(a:Int,b:Int):Int =
if(b==0) a else gcd(b, a % b)
}
定义运算符
Scala 中对方法的名称也没有什么特别的限制,你可以使用符号作为类方法的名称,比如使用 +、- 和 * 等符号。因此我们可以重新定义 Rational 如下:
class Rational (n:Int, d:Int) {
require(d!=0)
private val g =gcd (n.abs,d.abs)
val numer =n/g
val denom =d/g
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 this(n:Int) = this(n,1)
private def gcd(a:Int,b:Int):Int =
if(b==0) a else gcd(b, a % b)
}
scala 中的标识符
scala中可以使用两种形式的标识符,字符数字和符号。字符数字使用字母或是下划线开头,后面可以接字母或是数字,符号$在scala中也看做字母。然而以 $开头的标识符为保留的scala编译器产生的标识符所使用,程序应该避免使用以 $使用的标识符
符号标志符包含一个或多个符号,如 + 、 : 和 ? 。对于 +、++、:::、<、 ?>、 :-> 之类的符号,Scala 内部实现时会使用转义的标志符。例如对 :-> 使用 coloncolonminus$greater 来表示这个符号。因此,如果你需要在 Java 代码中访问 :-> 方法,你需要使用 Scala 的内部名称 $colonminusminusgreater 。
混合标志符由字符数字标志符后面跟着一个或多个符号组成,比如 unary_+ 为 Scala 对 + 方法的内部实现时的名称。
字面量标志符为使用 " 定义的字符串,比如 “x”、“yield”。 你可以在 " 之间使用任何有效的 Scala 标志符,Scala 将它们解释为一个 Scala 标志符,一个典型的使用是 Thread 的 yield 方法, 在 Scala 中你不能使用 Thread.yield() 是因为 yield 为 Scala 中的关键字, 你必须使用 Thread.“yield”()来使用这个方法。
方法重载
和 Java 一样,Scala 也支持方法重载,重载的方法参数类型不同却使用同样的方法名称,比如对于 Rational 对象, + 的对象可以为另外一个 Rational 对象,也可以为一个 Int 对象,此时你可以重载 + 方法以支持和 Int 相加。
def + (i:Int) =
new Rational (numer + i * denom, denom)
隐式类型转换
上面我们定义 Rational 的加法,并重载 + 以支持整数, r + 2 ,但如果我们需要 2 + r 如何呢?
x + 3 没有问题,3 + x 就报错了,这是因为整数类型不支持和 Rational 相加。
如果 Int 类型能够根据需要自动转换为 Rational 类型,那么 3 + x 就可以相加。Scala 通过 implicit def 定义一个隐含类型转换,比如定义由整数到 Rational 类型的转换如下:
implict def intToRational(x:Int) = new Rational(x)