scala--面向对象-继承-★★★★

extends、override、super

extends

  • Scala 中的继承与 Java 一样也是使用 extends 关键字;
  • 子类可以继承或覆盖父类的 field 和 method ,也可以实现子类特有的 field 和method

verride

  • Scala 中,如果子类要覆盖父类中的字段或方法,要使用 override 关键字;

super

  • 在子类中要调用父类中被覆盖的方法,要使用 super 关键字,显示的指出要调用的父类方法。
  • 注意
    • final修饰的类不能被继承
    • final修饰的字段或方法不能被覆盖
    • private 修饰的 field 和 method 不可以被子类继承
    • val字段使用 override 关键字覆盖
    • var字段使用 override 关键字重新赋值(注意不是重新定义)
    • override 关键字可以帮助开发者尽早的发现代码中的错误,如方法名拼写错误,参数错误等等,所以建议覆盖时加上override关键字;

扩展:protected说明

  • 和Java一样Scala 中也可以使用 protected 关键字来修饰 field 和 method。
  • 但是比Java要更严格一点:只有继承关系才可以访问,同一个包下,也是不可以的
  • 在子类中,可直接访问父类protected 修饰的 field 和 method,而不需要使用 super 关键字;
  • 还可以使用 protected[this] 关键字, 只允许在当前类中访问父类的 field 和 method,不允许通过其他子类对象访问父类的 field 和 method。
package cn.hanjiaxiaozhi.extendsdemo
​
/**
 * Author hanjiaxiaozhi
 * Date 2020/7/16 15:52
 * Desc 
 */
class Person {
    
    
  val name = "super"
  var age = 18def sayName = {
    
    
    println("Person--sayName")
  }def sayHello={
    
    
    println("hello")
  }
}
//Student继承了Person,将相当于有了Person中的非私有的一切
class Student extends Person{
    
    
  //如果子类要覆盖可以使用override
  override
  val name = "sub" //val修饰的可以覆盖的
​
  age = 20 //var修饰的可以重新赋值override
  def sayName = {
    
    
    //super.sayName //super表示调用父类的
    println("Student--sayName")
  }//sayHello如果不覆盖就直接继承过来了
  /*override
  def sayHello={
    println("hello")
  }*///子类也可以定义自己的变量
  val nickName = "xx"//子类也可以编写自己的方法
  def saybye(): Unit ={
    
    
    println("good bye~~" + nickName)
  }
}object Test{
    
    
  def main(args: Array[String]): Unit = {
    
    
    val s: Student = new Student()
    s.sayName
    s.sayHello
    s.saybye
    println("==================")
    val p: Person = new Student()//多态,父类类型接收子类对象,不需要强转
    p.sayName //本质是调用的是Student的
    p.sayHello//本质是调用的是Student的
    //p.saybye //如果使用的Person接收Student对象,那么不能调用Student中的方法
    //如果非得调用子类特有的方法得强转
    p.asInstanceOf[Student].saybye()
  }
}

类型判断、获取、转换

  • isInstanceOf 和 asInstanceOf
    • obj.isInstanceOf[XX类名]
      • 判断 obj 是否为 XX 类型的实例(判断对象是否为指定类以及其子类的实例)
      • 类似于Java中的: obj instanceof xx
    • obj.asInstanceOf[XX类名]
      • 把 obj 转换成 XX 类型的实例(new出来的父类不能强转为子类)
      • 类似于Java中的: (XX)obj
  • getClass 和 classOf
    • obj.getClass
      • 可以精确地获取对象的类型
      • 就是Java中的: 对象.getClass
    • classOf[XX类名]
      • 可以精确的获取类的类型
      • 类似于Java中的: 类名.class
  • 注意:
    • 如果对象是 null,
      • isInstanceOf 一定返回 false,
      • asInstanceOf 一定返回 null;
  • Scala与Java类型检查和转换类比
Scala Java
obj.isInstanceOf[XX类名] obj instanceof XX类名
obj.asInstanceOf[XX类名] (XX类名)obj
obj.getClass obj.getClass
classOf[XX类名] XX类名.class
  • 使用模式匹配进行类型判断
    • 在使用模式匹配进行类型判断(后面会讲模式匹配)
    • 功能上来说,与 isInstanceOf 的作用一样,主要判断是否为该类或其子类的对象即可,不是精准判断
    • 语法上来说,类似于 Java 中的 switch case
    • 实际的开发中,比如 spark 源码中,大量的使用了模式匹配进行类型判断,这种方式更加简洁明了、可维护、可扩展
package cn.hanjiaxiaozhi.extendsdemo
​
/**
 * Author hanjiaxiaozhi
 * Date 2020/7/16 16:22
 * Desc 
 */
class Person2 {
    
    
}class Student2 extends Person2
​
object Test2 {
    
    
  def main(args: Array[String]): Unit = {
    
    
    val p: Person2 = new Student2 //父类类型接收子类对象
    val s: Student2 = null
    //1. 对象.isInstanceOf[类名]表示判断对象是否属于某个类型或其子类型,如果对象为空返回null
    println(p.isInstanceOf[Student2]) //true
    println(p.isInstanceOf[Person2]) //true
    println(s.isInstanceOf[Student2]) //false
    println("====================")//2. 对象.getClass获取对象真实类型/字节码类型 == classOf[类名]是获取类的字节码类型
    println(p.getClass == classOf[Student2]) //true
    println(p.getClass == classOf[Person2]) //flase
    println("====================")//3. 对象.asInstanceOf[类型]--类型转换
    if (p.isInstanceOf[Student2]){
    
    
      val s2: Student2 = p.asInstanceOf[Student2]
      println("p已经转为Student2类型")
    }
  }
}
  • 总结
    • 和java的对比:
  • 判断
    • scala:对象.isInstanceOf[类名]
    • java: 对象 instanceof 类型
  • 获取
    • scala: 对象.getClass 或者 calssOf[类名]
    • java: 对象.getClass 或 类名.Class 或 Class.forName(“包类路径”)
  • 转换
    • scala: 对象.asInstanceOf[类型]
    • java: (类型)对象

构造器执行顺序

  • Scala中每个类都可以有一个主构造器和任意多个辅助构造器,
  • 辅助构造器的第一行都必须调用其他辅助构造器或者主构造器代码;
  • 子类的辅助构造器是一定不可能直接调用父类的构造器的,只能在子类的主构造器中调用父类的构造器。
  • 父类的构造函数已经定义过的字段,子类在使用时,不需要使用 val/var 来修饰,否则会被认为,子类要覆盖父类的字段
package cn.hanjiaxiaozhi.extendsdemo
​
/**
 * Author hanjiaxiaozhi
 * Date 2020/7/16 16:38
 * Desc 
 */
class Person3 (val name: String, val age: Int) {
    
    
  println("父类的主构造器")
  var score: Double = 0.0
  var address: String = "beijing"def this(name: String, score: Double) = {
    
    
    //每个辅助构造器的第一行都必须调用其他辅助构造器或者主构造器代码
    this(name, 30)
    println("父类的第一个辅助构造器")
    this.score = score
  }//其他辅助构造器
  def this(name: String, address: String) = {
    
    
    this(name, 100.0)
    println("父类的第二个辅助构造器")
    this.address = address
  }
}//子类的继承父类可以调用父类的任何一个构造器,只要保证最终调用的是父的主构造器即可
class Student3(name: String, score: Double) extends Person3(name, score){
    
    
  println("子类的主构造器")
}object Test3{
    
    
  def main(args: Array[String]): Unit = {
    
    
    val s = new Student3("jack",99.9)
    //父类的主构造器
    //父类的第一个辅助构造器
    //子类的主构造器
    println(s.name)
    println(s.age)
    println(s.score)
    println(s.address)
  }
}
  • 总结
    • Scala和Java一样都是先执行父的构造再执行子的构造
    • 在Scala中还有主和辅助构造器之分
    • 辅助构造器最终得调用主构造器

抽象类

  • 抽象类、抽象方法和抽象字段
    • 一个类中,如果含有一个抽象方法或抽象字段,就必须使用abstract将类声明为抽象类,该类是不可以被实例化的
    • 如果在父类中,有某些方法无法立即实现,而需要依赖不同的子类来覆盖,重写实现不同的方法此时,可以将父类中的这些方法编写成只含有方法签名,不含方法体的形式,这种形式就叫做抽象方法
    • 如果在父类中,定义了字段,但是没有给出初始值,则此字段为抽象字段
  • 注意
    • 在子类中覆盖抽象类的抽象方法时,可以不加override关键字,但建议加上;重写父类的非抽象方法,必须使用override关键字
    • 抽象类中可以有普通字段和方法

扩展:匿名内部类

  • 说明
    • 在Scala中匿名内部类是很常见的,Spark的源码中就大量的使用了匿名内部类;
    • 匿名内部类,就是定义一个没有名称的子类,并直接创建其对象,然后将对象的引用赋予一个变量
  • 通常还会将将该匿名内部类对象传递给其他方法或函数使用。
package cn.hanjiaxiaozhi.extendsdemo
​
/**
 * Author hanjiaxiaozhi
 * Date 2020/7/16 16:48
 * Desc 
 */
//abstract修饰的类称为抽象类
//里面可以有抽象方法或普通方法,也可以有抽象字段或普通字段
//有抽象字段/抽象方法的类一定是抽象类
//抽象类并不一定必须有抽象方法和抽象字段
abstract class Shape {
    
    
  //定义一个抽象字段,不用给值
  val name:String//定义一个抽象方法,不用给方法体,注意:得给返回类型,要不然就是Unit
  def getArea:Double}//定义一些图形继承抽象类Shape,实现getArea求面积的方法
class Square(val edge:Double) extends Shape {
    
    
  override val name: String = "正方形"override def getArea: Double = {
    
    
    edge * edge
  }
}
class Rectangle(val length:Double,val width:Double) extends Shape {
    
    
  override val name: String = "长方形"override def getArea: Double = {
    
    
    length * width
  }
}
class Cirle(val radius:Double) extends Shape {
    
    
  override val name: String = "圆形"override def getArea: Double = {
    
    
    math.Pi * radius * radius
  }
}
object Test4{
    
    
  def main(args: Array[String]): Unit = {
    
    
    val s1:Shape = new Square(2)
    val s2:Shape = new Rectangle(4,2)
    val s3:Shape = new Cirle(1)
    println(s1.name + " 面积为: " +s1.getArea)
    println(s2.name + " 面积为: " +s2.getArea)
    println(s3.name + " 面积为: " +s3.getArea)
    //正方形 面积为: 4.0
    //长方形 面积为: 8.0
    //圆形 面积为: 3.141592653589793
  }
}
​
​

猜你喜欢

转载自blog.csdn.net/qq_46893497/article/details/114041480