类的继承和多态

类的继承

继承是一种描述类与类之间的关系,反映的是“is a”这种关系。子类通过关键字extends继承了父类的字段和方法,同时可以自定义相应的字段和方法。

需要注意的是,如果父类中的成员,包括父类被final关键字修饰了,则无法被继承和覆盖。同样的,如果父类成员被private关键字修饰之后,子类也无法继承或是覆盖。

当有继承体系的时候,在用子类构造器创建对象的时候,先调用父类的构造器。

子类调用父类成员,如果子类没有重名的,直接成员名,如果有重名的,需要使用super来调用。也就是说,在父类中定义方法,通过“super.方法名”调用即可。

Scala中是无法直接调用父类构造器,是间接调用父类构造器,只能是主构造器调用父类的构造器,辅助构造器是不能直接调用父类的构造器。

// 基类
class Vehicle(speed: Int) {
  def race() = println("赛车")
}

// 派生类:只有主构造函数可以将参数传递给基构造函数
class RacingCar(speed: Int, color: String) extends Vehicle(speed) {
  override def race() = println("Racing Car")
  
  def info = println(s"速度:$speed, 颜色:$color")
}

// 另一个子类
class Bike(speed:Int, model:String) extends Vehicle(speed) {
  override def race() = println("Racing Bike")  
  def detail = println(s"速度:$speed, 品牌:$model")
}

object Demo3 {
  def main(args: Array[String]) {
    // 对象
    val vehicle1 = new RacingCar(200,"red")
    vehicle1.race
    vehicle1.info

    println("-----------------------")
    
    val vehicle2 = new Bike(100,"绿佳")
    vehicle2.race
    vehicle2.detail

    println("-----------------------")
    // 多态
    var veh: Vehicle = new RacingCar(250,"blue")
    println(veh.kph)
    veh.race

    veh = new Bike(150,"爱玛")
    println(veh.kph)
    veh.race
  }
}

在处理类和对象层次结构时,通常会有一个封装公共行为的基类。例如,让我们创建一个抽象类名Donut,它为printName定义了一个方法签名。

abstract class Donut(name: String) {
   def printName: Unit
}

任何扩展abstract类Donut的类都必须提供printName方法的实现。

要创建抽象Donut类的子类,必须使用extends关键字、传递name构造函数参数并为printName方法提供实现。如下面的示例所示,类VanillaDonut扩展了抽象类Donut,并提供了printName方法的实现。

// 伴生类
class VanillaDonut(name: String) extends Donut(name) {
   override def printName: Unit = println(name)
 }

// 伴生对象
object VanillaDonut {
   def apply(name: String): Donut = {
     new VanillaDonut(name)
   }

}

定义另一个子类GlazedDonut,从抽象类Donut继承。

class GlazedDonut(name: String) extends Donut(name) {
  override def printName: Unit = println(name)
}

object GlazedDonut {
  def apply(name: String): Donut = {
    new GlazedDonut(name)
  }
}

让我们创建两个Donut对象,一个使用VanillaDonut类,另一个使用GlazedDonut类。请注意,我们将vanillaDonut和glazedDonut的类型指定为基本类型Donut。

object InheritanceDemo {
  def main(args: Array[String]): Unit = {

    val vanillaDonut: Donut = VanillaDonut("Vanilla Donut")
    vanillaDonut.printName

    val glazedDonut: Donut = GlazedDonut("Glazed Donut")
    glazedDonut.printName
  }
}

case类继承

创建抽象Donut类的子类,必须使用extends关键字、传递name构造函数参数并为printName方法提供实现。

如下面的示例所示,case类VanillaDonut2扩展了抽象类Donut,并提供了printName方法的实现。

case class VanillaDonut2(name: String) extends Donut(name) {
  override def printName: Unit = println(name)
}

与之前的类继承不同,我们不需要为VanillaDonut2 case类创建一个伴生对象,因为case类将自动提供一个伴生对象。 创建另一个case类GlazedDonut2,它扩展了抽象Donut类。

case class GlazedDonut2(name: String) extends Donut(name) {
   override def printName: Unit = println(name)
}

同样,我们不需要为VanillaDonut2 case类创建一个伴生对象,因为case类将自动提供一个伴生对象。

让我们创建两个Donut对象,一个使用VanillaDonut2类,另一个使用GlazedDonut2类。请注意,我们将vanillaDonut2和glazedDonut2的类型指定为基本类型Donut。

object InheritanceDemo {
  def main(args: Array[String]): Unit = {

    val vanillaDonut2: Donut = VanillaDonut2("Vanilla Donut")
    vanillaDonut2.printName

    val glazedDonut2: Donut = GlazedDonut2("Glazed Donut")
    glazedDonut2.printName
  }
}

多态

在Scala继承中,Scala向父类的构造器传递参数。

Java中多态,父类引用指向子类对象,例如:

Fu zi = new zi();

Scala中多态的体现:

val zi:Fu = new zi()

多态类型,编译时看=的左边,运行要看=的右侧。

class Worker(name:String, age:Int) {

  var language:String = _

  def show(): Unit = {
    println(s"name is $name,, and age is $age")
  }

}

class Coder(name:String, age:Int, language:String) extends Worker(name, age) {

  def this() {
    this("程序员", 18, "Java")
  }

  override def show(): Unit = {
    println(s"name is $name, and age is $age, language is $language")
  }

}

val coder:Worker = new Coder("xpleaf", 23, "scala")
coder.show()

《Spark原理深入与编程实战》