继承的概念和基本原理

102 阅读4分钟

一.概念

  1. 定义:在原有类的基础上定义一个新类,原有类称为父类,新类称为子类。 class 子类名 extends 父类名 { 类体 }
  2. 好处:复用代码和实现多态。复用代码:子类可以继承父类的特性。多态 子类可以在自己内部实现父类没有的特性。
object class12 {
  // 伴生类
  class Boss {
    def power(): Unit = {
      println("Boss 有权利.....")
    }
    var car = "豪车"
  }
  //    子类     继承    父类
  class NoBody extends Boss {

  }
  def main(args:Array[String]): Unit = {
    val b = new NoBody()
    println(b.car)
    b.power()
  }
}

二.重写

  1. 概念:子类对父类的同名方法进行重写
  2. 当子类从父类继承的方法不能满足需要时,子类需要有自己的行为,怎么办?此时使用使用 override 可以重写父类的方法。
class Boss {
  def power(): Unit = {
    println("Boss 有权利.....")
  }
  var car = "豪车"
}
//    子类     继承    父类
class NoBody extends Boss {
  def run():Unit = {

  }

  override def power(): Unit = {
    println("老莫,我想吃鱼了")
  }
}
def main(args:Array[String]): Unit = {
  val b = new NoBody()
  println(b.car)
  b.power()
}

image.png

三.构造器

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

object class14 {
  class Father() {
   println("Father 构造器被调用")

    }
   class Son extends Father(){
     println("Son 构造器被调用")
   }
  def main(args:Array[String]): Unit = {
    //new Father()
    new Son()
  }
}
三-1.带参构造器
  1. 子类带参构造器:继承父类的属性。不用写val,var修饰符/自己的新属性,加上var,val修饰符
  2. 父类构造器:直接传入参数,不用写属性的类型
object class15 {
  class Dog(var name:String, var age:Int) {
    println(s"Dog ${name}, ${age} 构造器被调用")
  }
  class RuralDog(name:String,age:Int) extends Dog(name,age){
    println("RuralDog的构造器.....")
  }
  def main(args:Array[String]): Unit = {
    new RuralDog("旺财", 3)
  }
  class Dog(var name:String, var age:Int) {
    println(s"Dog ${name}, ${age} 构造器被调用")
  }
  class RuralDog(name:String,age:Int,var color:String) extends Dog(name,age){
    println("RuralDog的构造器.....")
  }
  def main(args:Array[String]): Unit = {
    new RuralDog("旺财", 3,"土黄色")
  }
}

image.png

你正在设计一个二维游戏的界面,需要用很多的点来表示游戏中的角色。 设计一个Point类,其x和y可以通过构造器提供。 它有几个方法:

  1. 方法1: 计算自己在哪个象限。 whereAmI():String
  2. 2.方法2: 计算和坐标原点的距离。getDist():Double
  3. 3.方法3: 计算与另一个点的距离。fromPoint(other:Point):Double
  4. 4.方法4: 重写equals 判断是否是同一个点(x和y都相等就是同一个点)。
  5. 5.方法5: 重写toString,更友好的输出点的信息。 再设计一个子类LabelPoint它来继承Point类,其构造器接收一个标签值和x,y坐标。 例如: New LabelPoint("black",1,2)
 // 二维游戏点坐标类(基础父类)
  class Point(val x: Double, val y: Double) {
    // 方法1:判断当前点所在象限(游戏中可用于区域划分、阵营判定等)
    def whereAmI(): String = {
      (x, y) match {
        case (0.0, 0.0) => "原点(无象限)"
        case (x, y) if x > 0 && y > 0 => "第一象限"
        case (x, y) if x < 0 && y > 0 => "第二象限"
        case (x, y) if x < 0 && y < 0 => "第三象限"
        case (x, y) if x > 0 && y < 0 => "第四象限"
        case (0.0, _) => "x轴上(无象限)"
        case (_, 0.0) => "y轴上(无象限)"
      }
    }

    // 方法2:计算到坐标原点的距离(游戏中可用于判定角色是否在技能范围、安全区等)
    def getDist(): Double = {
      math.sqrt(x * x + y * y)
    }

    // 方法3:计算与另一个点的距离(游戏中可用于角色间距判定、攻击范围检测等)
    def fromPoint(other: Point): Double = {
      val dx = x - other.x // 横向距离差
      val dy = y - other.y // 纵向距离差
      math.sqrt(dx * dx + dy * dy) // 欧氏距离公式
    }

    // 方法4:重写equals,坐标完全相同则判定为同一点(游戏中可用于判定是否重叠、碰撞等)
    override def equals(obj: Any): Boolean = {
      obj match {
        case other: Point => x == other.x && y == other.y // 仅比较坐标(忽略其他可能的扩展属性)
        case _ => false // 非Point类型直接返回不相等
      }
    }

    // 方法5:重写toString,友好输出点信息(游戏调试时便于查看坐标)
    override def toString(): String = {
      s"Point(坐标: ($x, $y), 象限: ${whereAmI()}, 到原点距离: ${getDist().formatted("%.2f")})"
    }

    // 可选:重写hashCode(与equals配套,避免集合中判重异常)
    override def hashCode(): Int = {
      val prime = 31
      var result = 1
      result = prime * result + x.hashCode()
      result = prime * result + y.hashCode()
      result
    }
  }

  // 带标签的游戏点类(子类,用于区分不同角色/物体,如"玩家"、"敌人"、"道具"等)
  class LabelPoint(val label: String, x: Double, y: Double) extends Point(x, y) {
    // 重写toString,增加标签信息(游戏中便于区分不同类型的点)
    override def toString(): String = {
      s"LabelPoint(标签: $label, 坐标: ($x, $y), 象限: ${whereAmI()}, 到原点距离: ${getDist().formatted("%.2f")})"
    }

    // 可选:扩展方法 - 判定当前点是否与同标签的点重叠(游戏中可用于同类型角色碰撞检测)
    def isSameLabelOverlap(other: LabelPoint): Boolean = {
      this.label == other.label && this.equals(other)
    }
  }

  // 游戏场景测试代码(验证功能可用性)
  object GamePointTest {
    def main(args: Array[String]): Unit = {
      // 1. 测试基础Point类
      val playerPos = new Point(3.0, 4.0) // 玩家坐标
      val enemyPos = new Point(-2.0, 5.0) // 敌人坐标
      val origin = new Point(0.0, 0.0) // 原点(比如出生点)

      println("=== 基础Point类测试 ===")
      println(playerPos) // 测试toString
      println(s"玩家与敌人的距离:${playerPos.fromPoint(enemyPos).formatted("%.2f")}") // 测试两点距离
      println(s"敌人是否在原点:${enemyPos.equals(origin)}") // 测试equals
      println(s"原点信息:$origin") // 测试原点的象限判定

      // 2. 测试LabelPoint子类
      val player = new LabelPoint("玩家A", 3.0, 4.0)
      val enemy = new LabelPoint("敌人B", -2.0, 5.0)
      val item = new LabelPoint("金币", 3.0, 4.0) // 与玩家同坐标的道具

      println("\n=== LabelPoint子类测试 ===")
      println(player) // 测试子类toString
      println(enemy)
      println(item)
      println(s"玩家与金币是否同标签重叠:${player.isSameLabelOverlap(item)}") // 测试扩展方法
      println(s"玩家与金币坐标是否相同:${player.equals(item)}") // 继承父类equals方法
      println(s"敌人到玩家的距离:${enemy.fromPoint(player).formatted("%.2f")}") // 继承父类距离方法
    }
}