1. 组合和继承
1) 抽象类
类关键字class之前加上abstract修饰符修饰的类即为抽象类。
示例:
abstract class AbstractClass {
def contents: Array[String]
}
abstract关键字说明有未实现的方法,因此不能实例化一个抽象类。
contents方法没有带abstract修饰飞,但它是抽象方法。方法的声明中不需要(也不允许)有抽象修饰符,一个方法只要没有实现(即没有等号或方法体),它就是抽象的。
2) 定义无参数方法
方法中没有参数列表,甚至空列表都没有的情况下,方法定义中不使用括号。
def width: Int
无参数方法是scala推荐的。推荐的惯例是无论何时,只要方法中没有参数并且方法仅能通过读取所包含对象的属性去访问可变状态(特指方法不能改变可变状态),就是无参数方法。这个惯例支持统一访问原则,也就是说客户端码不应该由属性是通过字段实现还是方法实现而受到影响。
因此,width方法可以修改为:
val width = contents(0).length
两种定义在客户端看了完全相同的。唯一的差别是访问字段比调用方法略快,因为字段值在类的初始化的时候被预计算,而方法调用的时候都需要计算。另一方面,使用字段需要为每个对象分配更多的内存空间。
3) 扩展类
与Java类似,在类名之后使用extends(继承)表示扩展。
继承表示超类的所有成员也是子类的成绩,但以下两种情况例外。第一,超类的私有成员不会被子类继承。第二,超类中的成员若与子类中实现的成员具有相同的名称和参数则不会被子类继承。这种情况被称为子类的成员重写了超类的成员。
2. Scala层级
1) 类层级
层级的顶端是Any类,定义了一下方法:
final def ==(that:Any):Boolean
final def !=(that:Any):Boolean
def equals(that:Any):Boolean
def hashCode:Int
def toString:String
因为每一个类都继承自Any,所有scala程序的每个对象都能用==,!=或equals比较,用hashCode做散列,以及用toString格式化。Any中的等号(==)、不等号(!=)被声明为final的,因此不能被重写。
Scala层级图:
Any有两个子类:AnyVal和AnyRef。AnyVal是scala每个内建值类的父类。有9个内建值类:Byte、Short、Char、Int、Float、Double、Long、Boolean和Unit。前8个都对应Java的基本类型,Unit大约对应Java的void类型,被用作不返回任何有趣结果的方法的结果类型。Unit只有一个实例值:()。
注意:scala的值类型都是scala.AnyVal的子类型,不是其他类的子类型,但是不同的值类型之间可以隐式地互相转换。
2) 底层类型
Scala的底层类型有两个类scala.Null和Scala.Nothing,它们是用统一的方式处理scala面向对象类型系统的某些“边界情况”的特殊类型。
Null类是null引用对象的类型,它是每个引用类的子类,Null不兼容值类型,因此不能将null赋值给值类型。
Nothing类型在scala的类层级的最底端,它是任何其他类型的子类型,然而根本没有这个类型的任何值。
3. 特质
特质是Scala里代码复用的基础单元,特质封装了方法和字段的定义,并可以通过混入到其他类中重用它们。
1) 特质定义
特质的定义出来使用关键字trait之外,与类定义无异。
示例:
trait TraitT {
def tMethod() {
println("Trait Test")
}
}
一旦特质被定义了,就可以使用extends或with关键字,把它混入类中。Scala程序员“混入特质”而不是继承它们,因为特质的混入与那些其他语言的多重继承有重要的差别。
示例:
class TTrait extends TraitT {
override def toString = "green"
}
TTrait类混入了TraitT特质,并且重写了toString方法。
特质同样也是类型,因此可以用在类型定义。
示例:
object ATrait extends App {
val t = new TTrait()
t.tMethod
val m:TraitT = t;
}
如果要把特质混入显式扩展了超类的类里,可以用extends指明待扩展的超类,用with混入特质。
2) 瘦接口对胖接口
特质的一种主要应用方式是可以根据类已有的方法自动为类添加方法。也就是说类可以丰富一个瘦接口,把它变成胖接口。