面向对象语言很重要的一个特点就是继承,我们来看看scala语言的继承有哪些特点。
extends关键字
首先,scala继承类的关键字和java一样,都是extends,比如:
class Person {
var name : String = ""
}
class Man extends Person { // Man继承自Person
var sex : Int = 0
}
}
final关键字的使用:
- 不想让类被继承,可以声明为final,这和java一样;
- 可以将类某一个字段或方法声明为final,表明不可被重写,这和java不一样,java中final变量相当于scala的val;
比如:
final class Person {
var name : String = ""
}
class Man extends Person { //error 编译时illegal inheritance from final class Person
var sex : Int = 0
}
构造函数
由于scala的构造函数有主构造函数和辅助构造函数,我们知道scala的辅助构造函数必须以对主构造函数或之前定义的辅助构造函数开始,因此,辅助构造函数无法直接调用父类的构造函数,那如何构造父类部分呢?答案是通过主构造函数。如果父类主构造器没有参数,则都是默认值,如果父类主构造器有参数,则必须在子类主构造器中指定参数。比如:
class Person (var name : String, var age : Int) () {
// body
}
class Student (var manName : String, var manAge : Int, var school : String) extends Person(manName, manAge) {
// body
}
在java中,一般是通过在构造函数中调用super来调用父类构造函数,在scala中,主构造器不能使用super方式,但是父类的普通函数依然可以通过super来调用,比如:
class Person (var name : String, var age : Int) () {
def print = name + " :" + age
}
class Student (var manName : String, var manAge : Int, var school : String) extends Person(manName, manAge) {
override def print = super.toString + ":" + school // 可以通过super来调用普通函数
}
访问控制
同java一样,我们可以将方法和字段声明为protected,这样字段或方法只能被任何子类访问,其他地方不可访问。
这个protected和java的不太一样,另,还有一个protected[this]可以将访问权限控制在当前对象。
重写
- scala可以重写父类方法,但需要override关键字,类似于java中可选的@Override注解,如上例中Student类重写print方法,需要override关键字。
- scala和java不一样的是,也可以重写字段。那val,var和不带参数的def之间的重写什么规则呢。
重写字段 | 用val重写 | 用var重写 | 用def重写 |
---|---|---|---|
val字段 | 可以,子类有一个同名字段 | 不可以 | 不可以 |
var字段 | 不可以 | 仅当父类的var字段是抽象的才可以 | 不可以 |
def无参函数 | 可以,子类有一个字段和对应getter重写父类方法 | 可以,需重写getter和setter对 | 可以,同重写函数 |
抽象类
- scala也可以使用abstract关键字将类声明为抽象类,但是关键字不能用于方法。
- 抽象类也可以有抽象字段,即没有初始化的字段,子类必须提供具体的字段。
比如:
abstract class Person {
val name : String // 抽象val字段
var age : Int // 抽象var字段
def print // 抽象函数
}
class Student extends Person {
val name = "name" // 不需要override字段
var age = 10 // 不需要override字段
def print = name + ":" + age // 不需要override字段
}
scala类继承层次
scala类的继承层次如下图所示:
基本类型以及Unit类型,都继承自AnyVal,AnyRef和java的Object对应,是所有对象类的基类。而他们都继承自Any类,继承层次跟节点。
Any类定义了isInstanceOf、asInstanceOf、equals、hashCode等,AnyRef额外定义了Object类的监视方法wait、notify和notifyAll,同时提供synchronized方法(等同于java的synchronized块)。
类型检查和转换
其中isInstanceOf用来判断对象是否是某个类,asInstanceOf方法将引用转换为子类的引用,比如:
val x = 1
var ret = if (x.isInstanceOf[Int]) {
x.asInstanceOf[Int] + 1
} else if (x.isInstanceOf[Long]) {
x.asInstanceOf[Long]
} else {
-1
}
类型检查和类型匹配,可以使用match,这样效果更好,比如上面的可以改为:
val x = 1
var ret = x match {
case i : Int => i + 1
case l : Long => l
case _ => -1
}