Scala学习2:继承

1.类的继承

(1) 重写属性或者方法时,要使用关键字override

(2) final修饰的属性或者方法不能被重写

(3) 重写规则:

a. 父类中的val属性,子类只能使用用val属性来重写

class Parent {
  val str: String = "aaa"
}
class Child extends Parent {
  override val str: String = "bbb"
}

b. 父类无参数的def方法,子类可以使用def方法重写,也可以使用val属性重写

class Parent {
  def getStr: String = "aaa"
  def getNum() = {
    1
  }
}
class Child extends Parent {
  override val getStr: String = "bbb"
  override val getNum: Int = 2
}

c. 父类有参数的def方法,子类只能使用def方法来重写

d. 父类的get set方法,子类能使用val属性来重写

class Parent {
  def str: String = "aaa"
  def str_=(s: String) = { //set方法_与=之间不能有空格
    
  }
}
class Child extends Parent {
  override var str: String = "bbb"
}

e. 父类的var属性,子类不能重写

(4) 主构造器的继承

如果父类带有主构造器,子类的主构造器必须覆盖父类构造器的所有参数

class Parent(val str: String) {
  
}
class Child(str: String, val num: Int) extends Parent(str) {
  
} 

2.抽象类的继承

(1) 抽象类使用abstract修饰,其中可以定义没有初始值的属性和没有方法体的方法

(2) 重写抽象类属性和方法可以不使用override(但最好还是加上)

(3) 重写规则

a.抽象父类中没有初始值的var属性,可以使用var属性重写

abstract class Parent(val str: String) {
  var flag: Boolean
  val ch: Char
  def func: String
}
class Child(str: String, val num: Int) extends Parent(str) {
  override var flag = true
  override val ch = 'a'
  override val func = "abc"
} 

(4) 子类必须实现父类中未实现的属性或者方法(如果子类也是个抽象类,那就不必了)

3.特质的继承

(1) 特质使用trait修饰,其中可以定义没有初始值的属性和没有方法体的方法

(2) 特质的继承方式

a. 类继承特质extends…with…with…(extends后可以接类、抽象类、特质,with后只能接特质)

与继承抽象类相同。特质中已赋值的属性/方法直接继承到子类中,未赋值的属性/方法要在子类中重写。

trait Attr1 {
  val str: String
}
trait Attr2 {
  def func
}
class Child extends Attr1 with Attr2 {
  override val str = "abc"
  override def func {
    
  }
}

b. 对象继承特质new…with…with…(new后接类名,with后接特质)

特质中已赋值的属性/方法直接继承到对象中,未赋值的属性/方法要在对象中被覆盖(不是重写哦)。

trait Attr1 {
  val str: String = "abc"
}
trait Attr2 {
  def str: String
  def getStr {
    println("attr2=" + str)
  }
}
class Child {
  
}
object Main {
  def main(args: Array[String]): Unit = {
    val child = new Child with Attr1 with Attr2
    child.getStr
  }
}

当然,这两种继承方式是可以混用的。

4.有意思的问题

(1) 继承中的属性初始化顺序

class Parent {
  val size = 1
  val arr = new Array[String](size)
}
class Child extends Parent {
  override val size = 2
}
object Main {
  def main(args: Array[String]): Unit = {
    val child = new Child
    println(child.arr.length)
  }
}

输出的结果即不是1也不是2,而是0。因为父类属性初始化是在子类的环境下,所以arr构造使用的size是子类的。而在父类初始化完成之前,子类的属性是没有初始化的,所以子类的size是0。

两种方式解决该问题:

使用lazy

class Parent {
  val size = 1
  lazy val arr = new Array[String](size)
}
class Child extends Parent {
  override val size = 2
}
object Main {
  def main(args: Array[String]): Unit = {
    val child = new Child
    println(child.arr.length)
  }
}

lazy修饰的属性,只在调用时才会初始化,所以输出的结果是2。

提前初始化

class Parent {
  val size = 1
  val arr = new Array[String](size)
}
class Child extends {override val size = 2} with Parent {
  
}
object Main {
  def main(args: Array[String]): Unit = {
    val child = new Child
    println(child.arr.length)
  }
}

使用extends…with…语法来进行提前初始化。extends{}中的表达式会在父类初始化前就进行,所以输出的结果是2。

(2) 特质继承,多个特质中含有相同的属性/方法

a. 类继承特质

子类重写了该属性/方法:就不会报错。

子类没重写该属性/方法:特质中一个属性/方法是有值的,其他都是未赋值的,就不会报错,该属性/方法以那个有值的为准。其他情况都报错。

b. 对象执行特质

对象中有该属性/方法:特质中所有属性/方法都不能有值,否则会报错。

对象中无该属性/方法:特质中有且只能有一个属性/方法有值,否则会报错。

可以为多个有相同属性/方法的特质添加一个父特质,这样多个相同属性/方法就不会冲突了:

trait BaseAttr {
  val str: String
}
trait Attr1 extends BaseAttr {
  override val str: String = "abc"
}
trait Attr2 extends BaseAttr {
  override val str: String = "def"
}

但是相同属性/方法会被覆盖,顺序是越靠后的特质会覆盖前面的特质:

class Child1 extends Attr1 with Attr2 {

}
class Child2 {
  
}
object Extend {
  def main(args: Array[String]): Unit = {
    val child1 = new Child1
    println(child1.str) // def
    val child2 = new Child2 with Attr1 with Attr2
    println(child2.str) // def
  }
}

(3) 抽象重写

这种场景出现在特质中(貌似是因为特质的方法是动态绑定的,所以父特质未实现方法也可以在子特质中调用)

trait Parent {
  def func
}
trait Child extends Parent {
  abstract override def func {
    ...
    super.func
    ...
  }
}

这种场景有点像AOP,在super.func的前后插入切面。因为super.func是未实现的,所以要用abstract override。

5.如何选择?是抽象类还是特质

看了上面的介绍,是不是觉得抽象类和特质特别的相似。那什么时候使用抽象类,什么时候使用特质呢?

1. 多重继承,当然要使用特质了。

2. 需要有构造方法的,要使用抽象类,因为特质没有构造方法。

3. 会被Java继承,要使用抽象类。

1 Reply to “Scala学习2:继承”

发表评论