类的继承和多态
类的继承
继承是一种描述类与类之间的关系,反映的是“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()