Scala 类的继承

22 阅读3分钟

继承

    1. 类和对象(一个类 多个对象)
    1. new
    1. 构造函数 构造器
    1. 辅助构造器
    1. private override toString equals this
    1. apply 单列模式
    1. 伴生类 伴生对象

    1. 多个类
    1. 继承 : class 子类 extend 父类
    1. 好处:不劳而获。 子类可以直接使用父类的属性和方法
    1. 子类对父类方法的 重写 : 在子类中通过override 覆盖(重写)父类的同名方法
    1. super 在子类中访问父类
    1. 面向对象编程的三大特征: 封装,继承,多态
    1. 构造器的调用顺序:先调用父类的 再调用子类的

(一)继承的概念和基本语法

定义:在原有类的基础上定义一个新类,原有类称为父类,新类称为子类。

class 子类名 extends 父类名 { 类体 }

继承的特点:Dog就直接具备了animal的功能eating。

代码演示:

class Animal() {
  def eat(): Unit = {
    println("animal eat...")
  }
}
class Dog() extends Animal() {

}

def main(args: Array[String]): Unit = {
  val d1 = new Dog()
  d1.eat()
}

运行结果:

(二)继承的好处

好处:复用代码和实现多态。不劳而获:子类可以直接使用父类的属性和方法

复用代码:  子类可以继承父类的特性。

多态:  子类可以在自己内部实现父类没有的特性。

代码演示:

// 动物
class Animal(var name:String = "动物") {

  val age:Int = 10;

  def eat(): Unit = {
    println("animal eat...")
  }
  def run():Unit = {
    println("animal run...")
  }
}

// 狗
class Dog() extends Animal() {

}

def main(args: Array[String]): Unit = {
  val d1 = new Dog()
  d1.run()
  d1.eat()
}

(三)继承的方法重写

子类对父类方法的 重写:在子类中通过override 覆盖(重写)父类的同名方法
super 在子类中访问父类

代码演示:

class Parent() {
  val name:String = ""
  def run():Unit = {
    println("run....")
  }
}

class Son extends Parent(){

  // 如果希望对父类的方法进行改进: 觉得不好
  def run1():Unit = {
    println("开自动驾驶的车 run...")
  }

  override def run(): Unit = {
    super.run() //super.run 在子类中 调用父类的方法
    println("开自动驾驶的车 run...")
  }
}

def main(args: Array[String]): Unit = {
  val s1 = new Son()
  s1.run()
}

代码说明:在子类的内部,使用super来访问父类

(四)继承与多态

面向对象的三个特点:封装  继承 多态

代码演示:

class Fruit() {
  def eat():Unit = {
    println("eat......")
  }
}

class Apple extends Fruit {
  override def eat(): Unit = {
    println("吃掉果皮 中间的不能吃")
  }
}

class Watermelon extends Fruit {
  override def eat(): Unit = {
    println("削皮 中间的最好吃")
  }
}

def main(args: Array[String]): Unit = {
  def test(fruit: Fruit):Unit = {
    fruit.eat()
  }
  val a1 = new Apple()
  test(a1)

  val w1 = new Watermelon()
  test(w1)
}

(五)处理构造器的调用顺序

构造器的调用顺序:先调用父类的 再调用子类的

代码演示:

class Father() {
  println("Father 的构造器....")
}

class Son extends Father() {
  println("Son 的构造器.....")
}

def main(args: Array[String]): Unit = {
  // 创建一个子类的对象
  // 先调用父类的构造器 → 子类的构造器
  new Son
}

(六)当父类 子类都存在带参数的构造器的时候 如何进行参数传递?

代码:

// 父类
class Animal(var name:String, var age:Int) {
  println(s"父类的构造器被调用...${name}, ${age}")
  def say():Unit = {
    println(s"Animal ${name}, ${age}")
  }
}

// 子类
class Dog(name:String, age:Int, var color:String) extends Animal(name, age) {
  override def say(): Unit = {
    super.say()
    println(s"狗狗...,我的颜色是${color}")
  }
}

def main(args: Array[String]): Unit = {
  val d1 = new Dog("旺财", 1, "黑色")
  d1.say()
}

(案例)设计一个Point类,其x和y可以通过构造器提供

它有几个方法:

  1. 方法l:计算自己在哪个象限。whereAmI():String
  2. 方法2:计算和坐标原点的距离。getDist():Double
  3. 方法3:计算与一个点的距离。fromPoint(other:Point):Double
  4. 方法4:重写equals判断是否是同一个点(x和y都相等就是同一个点)。
  5. 方法5:重写toString,更友好的输出点的信息。

例如:New.LabelPoint("black",1.2)

class Point(var x:Double, var y:Double) {

  def whereAmI():String = {
    if(x > 0 && y > 0) {
      "第一象限"
    } else if(x < 0 && y < 0) {
      "第二象限"
    } else if(x < 0 && y < 0) {
      "第三象限"
    } else if (x > 0 && y < 0) {
      "第四象限"
    } else if (x == 0 && y != 0) {
      "y轴上"
    } else if (x != 0 && y == 0) {
      "x轴上"
    } else if(x==0 && x == 0) {
      "原点"
    } else {
      "未知"
    }
  }

  def getDist():Double= {
    Math.sqrt(this.x * this.x + this.y * this.y)
  }
  def fromPoint(other:Point):Double= {
    Math.sqrt((this.x-other.x) * (this.x-other.x) + (this.y-other.y))
  }

  override def equals(obj: Any): Boolean = {
    val other = obj.asInstanceOf[Point]
    this.x == other.x && this.y == other.y
  }

  override def toString:String = {
    s"Point(${x},${y})"
  }

}

def main(args: Array[String]): Unit = {
  val p1 = new Point(1,1)
  val p2 = new Point(1,1)
  println(p1 == p2)
  println(p1.whereAmI())
  println(p1.getDist())
  println(p1.fromPoint(p2))

}