Scala--继承

  关于scala继承,其实很简单,基本思想和java都是相同的。主要不同在于,一些小小的细节,比如重写是需要用override,只有主构造器可以调用超类的主构造器等。

重写

继承就是从已有的类中通过extends关键字派生出新的类,已有的类叫父类,新派生出的类叫子类。子类就拥有父类非私有的属性和行为,并能扩展新的属性和行为。重写(override)就是子类去重新定义父类的属性和行为。

  1. 继承父类使用extends关键字
  2. 重写父类非私有属性和行为(非抽象方法)时必须使用override
  3. 调用父类的方法时使用super关键字,字段不需要
  4. 私有(private)方法和属性子类无法获取和重写
  5. 受保护(protected)方法或属性只有子类能获取和重写
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/**
* 继承
* Created by xiaoxiaomo on 2016/4/4.
*/
class Parent {
//父类
val name = "xiaomo";//通过val重写
val age = 25 ; //通过val重写
var height = 175.0 ;//子类没有重写
private val six = 0 ;//私有字段
protected val money = 9999999;//protected

//无参方法
def supSex = six ;

//say方法
def say(info:String){
println("Parent say ,"+ info)
}

}

class SubClass extends Parent{ //子类
//val重写父类val参数
override val name = "xiaoxiaomo"
override val age = 24 ;
override val money = 99999

//val重写不带参数的def
//调用父类方法使用super关键字
override val supSex: Int = {
if( super.supSex == 0 )
1
else 0
}

//def只能重写一个def方法
//访问父类字段不需要使用supper,直接访问即可
override def say(info : String): Unit = {
println("SubClass say ,"+ info + " age:"+age +" name:"+name )
println("height:"+height + " money:"+money)
}
}

//Test
object Test2 extends App{
new SubClass().say("Hello!")
print(new SubClass().supSex)
}
  • 运行结果
1
2
3
SubClass say ,Hello! age:24 name:xiaoxiaomo
height:175.0 money:99999
1
  • 在每一个对象中,我们隐式继承java.lang.Object的一些方法。这些方法我们同样可以去重写,常见的是去重写toString()方法equals()方法,如下图所示:

  • 注意(重写的限制) :
  1. def只能重写另一个def
  2. val只能重写另一个val或不带参数的def
  3. var只能重写另一个抽象的var(下面抽象类中会讲到)

超类构造器

对于构造器,在前面Scala–类的博文中已经讲解,它和java的构造器是有一定区别的。

  1. 因为子类辅助构造器必须调用子类主构造器,所以子类辅助构造器就无法调用父类的构造器
  2. 所以就必须得子类主构造器去调用超类的构造器
  3. 由于主构造器和类交织在一起,所以调用父类构造器也会和类交织在一起
  • 来看一下下面的事例,1、首先定义一个父类,父类的主构造器数(有参数age),一个辅助构造器(有参数name和age)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

package com.xiaoxiaomo.demo.ch08

/**
* 继承-构造器
* Created by xiaoxiaomo on 2016/4/4.
*/
class ConstructorPartDemo(val age :Int) {

val name = "xiaoxiaomo"
val height = 178 ;
println("父类主构造器")

//辅助构造器
def this( name : String, age : Int){
this(age)//必须在构造函数首行
println("父类,辅助构造器,name:"+name+" age:"+age+" height:"+height)
}
}
  • 定义三个子类,第一个继承超类的主主构造器,第二个继承超类的辅助构造器,第三个想继承父类的无参构造器,但由于超类没有无参的构造器,所以不会通过编译,下面来看一看代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

/**
* 第一个子类
* 继承超类的主构造器
* @param age 传递给父类
* @param name
*/
class ConstructorSub1(age:Int,override val name:String) extends ConstructorPartDemo(age){

println("子类1,主构造器")

//主构造器重已经重写了字段name,下面就不能再次重写
//override val name = "xiaoxiaomo"
//辅助构造器
def this( name : String, age : Int){
this(age,name)
println("子类1,辅助构造器,name:"+name+" age:"+age+" height:"+height)
}
}

/**
* 第二个子类
* 继承超类的辅助构造器
* @param age 传递给父类
* @param name 传递给父类
* @param height
*/
class ConstructorSub2(age:Int,override val name:String,height:Double) extends ConstructorPartDemo(name,age){


println("子类2,主构造器")

//辅助构造器
def this( name : String, age : Int){
this( age , name , 175 )
println("子类2,辅助构造器,name:"+name+" age:"+age + " height:"+height)
}
}

/**
* 第三个子类,无法编译
*/
//下面写法是不会通过编译的
//因为没有ConstructorPartDemo()的无参构造器
//class ConstructorSub3(age:Int,override val name:String) extends ConstructorPartDemo(){
//}


//TEST
object Test extends App{
new ConstructorSub1("one-snail",11)
println("----------------")
new ConstructorSub2("two-snail",22)
}
  • 通过调用Test,运行结果:
1
2
3
4
5
6
7
8
父类主构造器
子类1,主构造器
子类1,辅助构造器,name:one-snail age:11 height:178
----------------
父类主构造器
父类,辅助构造器,name:two-snail age:22 height:178
子类2,主构造器
子类2,辅助构造器,name:two-snail age:22 height:175.0

抽象类

抽象类,往往用来描述一个事物的抽象概念,不能具体指定为某一实体(不能实例化)。比如,我们所看到的食物苹果和梨,我们也可以统称为水果,苹果和梨就是具体的东西,水果(不能具体指某一实物)就是一个苹果和梨的抽象的概念。
申明抽象类使用abstract,在scala中,抽象类和类中的方法、字段和类型都可以是抽象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

/**
* 抽象类
* Created by xiaoxiaomo on 2016/4/4.
*/
abstract class AbstractDemo {

//没有初始化值,这是一个带有get方法的抽象字段
val id:Int
//没有初始化值,这是一个带有get/set方法的抽象字段
var age:Int

//不需要使用override
def name:Unit ; //没有方法体

//不需要使用override
//带参数的方法
def sayHi( who:String ) ; //没有方法体

//不需要使用override
//带参数和返回值(Int)的方法
def sum( a:Int ,b:Int ) : Int; //没有方法体

//这不是一个抽象方法而是一个普通方法
def getOther(): Unit ={
println("这个方法自己有方法体")//有方法体
}
}
  • 实现和测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//实现抽象类
class AbstractImp extends AbstractDemo{
val id: Int = 17

//不需要使用override
def sum(a: Int, b: Int): Int = {
a + b
}

//不需要使用override
def name = println("xiaomo")

//不需要使用override
def sayHi(who: String): Unit = {
println("Hello everyone! My name is "+who + " And "+ age + " years old!")
getOther() ;//调用自己的方法
}

var age: Int = 25

//这不是一个抽象方法而是一个普通方法
override def getOther(): Unit = super.getOther()
}

//TEST
object Test3 extends App{
val abs = new AbstractImp()
abs.name
println(abs.sum(7,8)) //调用求和方法
abs.sayHi("xiaoxiaomo")//sayHi方法
}
  • 运行结果
1
2
3
4
xiaomo
15
Hello everyone! My name is xiaoxiaomo And 25 years old!
这个方法自己有方法体
  • 抽象字段:具体类中给字段申明的时候不给字段赋值,该字段变为抽象字段
  • 抽象方法:抽象方法不需要(也不允许)有abstract修饰符一个方法只要是没有实现(没有等号或没有方法体),它就是抽象的在子类中覆写或者覆写接口中的非抽象方法(方法有具体实现)要使用override关键字
  • 抽象类型:scala中的类型成员也可以是抽象的。抽象字段和抽象方法都是只有字段或者方法的定义,而没有字段或者方法的具体实现

Scala继承层级

(图片来源于网络)

  1. Any类:是整个继承层级的跟,下面有AnyVal和AnyRef
  2. AnyVal:包含了所用值类型以及Unit,
  3. AnyRef:相当于java的Object,所用的其他都是AnyRef的子类
  • AnyVal

AnyVal并没有追加任何方法,它只是所有值类型的一个标记

  • AnyRef

AnyRef,追加了来之Object类的监视方法wait和notify/notifyAll,同时还提供了一个带函数参数的方法synchronized。

  • ScalaObject

所用的Scala类都实现ScalaObject这个标记接口,这个接口没有定义任何方法

  • Nothing/Null
  1. 在继承的另一端是Nothing和Null类型
  2. Null类型的唯一实例是null值,可以将null赋值给任何引用类型,但不能赋值给值类型的变量。
  3. Nothing类型没有实例,他对于泛型结构时常用,空列表Nil的类型是List[Nothing],它是List[T]的子类型。

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器