2021年大数据常用语言Scala(三十):scala面向对象 继承(extends) 

举报
Lansonli 发表于 2021/09/28 23:35:01 2021/09/28
【摘要】   目录 继承(extends)  简单继承 override和super isInstanceOf和asInstanceOf getClass和classOf 访问修饰符 调用父类的constructor 抽象类 抽象方法 抽象字段 匿名内部类 继承(extends)  简单...

 

目录

继承(extends) 

简单继承

override和super

isInstanceOf和asInstanceOf

getClass和classOf

访问修饰符

调用父类的constructor

抽象类

抽象方法

抽象字段

匿名内部类


继承(extends) 

简单继承

scala和Java一样,使用extends关键字来实现继承。可以在子类中定义父类中没有的字段和方法,或者重写父类的方法。

示例1:实现简单继承


  
  1. class Person {
  2.   var name = "super"
  3.   def getName = this.name
  4. }
  5. class Student extends Person
  6. object Main13 {
  7.   def main(args: Array[String]): Unit = {
  8.     val p1 = new Person()
  9.     val p2 = new Student()
  10.     p2.name = "张三"
  11.     println(p2.getName)
  12.   }
  13. }

 

示例2:单例对象实现继承


  
  1. class Person {
  2.   var name = "super"
  3.   def getName = this.name
  4. }
  5. object Student extends Person
  6. object Main13 {
  7.   def main(args: Array[String]): Unit = {
  8.     println(Student.getName)
  9.   }
  10. }

override和super

  • 如果子类要覆盖父类中的一个非抽象方法,必须要使用override关键字
  • 可以使用override关键字来重写一个val字段(var无需重写,直接改值即可)
  • 可以使用super关键字来访问父类的成员

 

示例1:class继承class


  
  1. class Person {
  2.   val name = "super"
  3.   def getName = name
  4. }
  5. class Student extends Person {
  6.   // 重写val字段
  7.   override val name: String"child"
  8.   // 重写getName方法
  9.   override def getNameString"hello, " + super.getName
  10. }
  11. object Main13 {
  12.   def main(args: Array[String]): Unit = {
  13.     println(new Student().getName)
  14.   }
  15. }


注意,当字段被子类覆盖的时候,用super无法访问父类字段会报错,或者不报错也无法访问到父类的原始值,只能访问到覆盖的值哦。
如上代码,通过getName访问父类的字段也无法访问到super这个值,因为子类对字段值进行覆盖,得到的就只能是child这个值。

 

isInstanceOf和asInstanceOf

我们经常要在代码中进行类型的判断和类型的转换。在Java中,我们可以使用instanceof关键字、以及(类型)object来实现,在scala中如何实现呢?

 

scala中对象提供isInstanceOf和asInstanceOf方法。

  • isInstanceOf判断对象是否为指定类的对象
  • asInstanceOf将对象转换为指定类型

 


  
  1. class Person3
  2. class Student3 extends Person3
  3. object Main3 {
  4.   def main(args: Array[String]): Unit = {
  5.     val s1:Person3new Student3
  6.     // 判断s1是否为Student3类型
  7.     if(s1.isInstanceOf[Student3]) {
  8.       // 将s1转换为Student3类型
  9.       val s2 =  s1.asInstanceOf[Student3]
  10.       println(s2)
  11.     }
  12.   }
  13. }

 

getClass和classOf

 

isInstanceOf 只能判断出对象是否为指定类以及其子类的对象,而不能精确的判断出,对象就是指定类的对象。如果要求精确地判断出对象就是指定类的对象,那么就只能使用 getClass 和 classOf 。

 

  • p.getClass可以精确获取对象的类型
  • classOf[x]可以精确获取类型
  • 使用==操作符就可以直接比较

 

示例:


  
  1. class Person4
  2. class Student4 extends Person4
  3. object Student4{
  4.   def main(args: Array[String]) {
  5.     val p:Person4=new Student4
  6.     //判断p是否为Person4类的实例
  7.     println(p.isInstanceOf[Person4])//true
  8.     //判断p的类型是否为Person4类
  9.     println(p.getClass == classOf[Person4])//false
  10.     //判断p的类型是否为Student4类
  11.     println(p.getClass == classOf[Student4])//true
  12.   }
  13. }

访问修饰符

Java中的访问控制,同样适用于scala,可以在成员前面添加private/protected关键字来控制成员的可见性。但在scala中,没有public关键字,任何没有被标为private或protected的成员都是公共的。

 

private[this]修饰符

被修饰的成员只能在当前类中被访问。或者可以理解为:只能通过this.来访问(在当前类中访问成员会自动添加this.)。

 

示例:

创建一个Person类

    • 添加一个private[this]修饰符的name字段,并赋值为"super"
    • 添加一个getName方法,获取name字段
    • 添加一个sayHelloTo方法,接收一个Person类型参数,尝试打印该参数的name字段

创建一个Person的伴生对象

    • 添加一个showName方法,接收一个Person类型参数,尝试打印该参数的name字段

 

代码:


  
  1. class Person {
  2.   // 只有在当前对象中能够访问
  3.   private[thisvar name = "super"
  4.   def getName = this.name // 正确!
  5.   def sayHelloTo(p:Person) = {
  6.     println("hello" + p.name)     // 报错!无法访问
  7.   }
  8. }
  9. object Person {
  10.   def showName(p:Person) = println(p.name)  // 报错!无法访问
  11. }

 

protected[this]修饰符

被修饰的成员只能在当前类和当前子类中被访问。也可以理解为:当前类通过this.访问或者子类通过this.访问

 

示例:

将Person类的name字段访问修饰符改为protected[this]

创建一个Student类

    • 添加一个showName方法,在方法中访问name字段
    • 添加一个sayHelloTo2方法,接收一个Person类型参数,在方法中打印该参数的name字段

  
  1. class Person {
  2.   // 只有在当前对象以及继承该类的当前对象中能够访问
  3.   protected[thisvar name = "super"
  4.   
  5.   def getName {
  6.     // 正确!
  7.     this.name
  8.   }
  9.   def sayHelloTo1(p:Person) = {
  10.     // 编译错误!无法访问
  11.     println(p.name)
  12.   }
  13. }
  14. object Person {
  15.   def sayHelloTo3(p:Person) = {
  16.     // 编译错误!无法访问
  17.     println(p.name)
  18.   }
  19. }
  20. class Student extends Person {
  21.   def showName {
  22.     // 正确!
  23.     println(name)
  24.   }
  25.   def sayHelloTo2(p:Person) = {
  26.     // 编译错误!无法访问
  27.     println(p.name)
  28.   }
  29. }

 

调用父类的constructor

实例化子类对象,必须要调用父类的构造器,在scala中,只能在子类的主构造器中调用父类的构造器

步骤:

创建一个Person类,编写带有一个可变的name字段的主构造器

创建一个Student类,继承自Person类

    • 编写带有一个name参数、clazz班级字段的主构造器
    • 调用父类的构造器

创建main方法,创建Student对象实例,并打印它的姓名、班级

 

代码:


  
  1. class Person5(var name:String)
  2. // 直接在父类的类名后面调用父类构造器
  3. class Student5(name:Stringvar clazz:Stringextends Person5(name)
  4. object Main5 {
  5.   def main(args: Array[String]): Unit = {
  6.     val s1 = new Student5("张三""三年二班")
  7.     println(s"${s1.name} - ${s1.clazz}")
  8.   }
  9. }

 

抽象类

如果类的某个成员在当前类中的定义是不包含完整的,它就是一个抽象类

不完整定义有两种情况:

方法没有方法体

变量没有初始化

没有方法体的方法称为抽象方法,没有初始化的变量称为抽象字段。定义抽象类和Java一样,在类前面加上abstract关键字就可以了

 

抽象方法

示例1:

 

设计4个类,表示上述图中的继承关系,每一个形状都有自己求面积的方法,但是不同的形状计算面积的方法不同。

 

步骤:

创建一个Shape抽象类,添加一个area抽象方法,用于计算面积

创建一个Square正方形类,继承自Shape,它有一个边长的主构造器,并实现计算面积方法

创建一个长方形类,继承自Shape,它有一个长、宽的主构造器,实现计算面积方法

创建一个圆形类,继承自Shape,它有一个半径的主构造器,并实现计算面积方法

编写main方法,分别创建正方形、长方形、圆形对象,并打印它们的面积

 

代码:


  
  1. // 创建形状抽象类
  2. abstract class Shape {
  3.   def area:Double
  4. }
  5. // 创建正方形类
  6. class Square(var edge:Double /*边长*/) extends Shape {
  7.   // 实现父类计算面积的方法
  8.   override def area: Double = edge * edge
  9. }
  10. // 创建长方形类
  11. class Rectangle(var length:Double /*长*/var width:Double /*宽*/) extends Shape {
  12.   override def area: Double = length * width
  13. }
  14. // 创建圆形类
  15. class Cirle(var radius:Double /*半径*/) extends Shape {
  16.   override def area: Double = Math.PI * radius * radius
  17. }
  18. object Main6 {
  19.   def main(args: Array[String]): Unit = {
  20.     val s1:Shape = new Square(2)
  21.     val s2:Shape = new Rectangle(2,3)
  22.     val s3:Shape = new Cirle(2)
  23.     println(s1.area)
  24.     println(s2.area)
  25.     println(s3.area)
  26.   }
  27. }

 

抽象字段

示例2:

 

步骤:

创建一个Person抽象类,它有一个String抽象字段WHOAMI

创建一个Student类,继承自Person类,重写WHOAMI字段,初始化为学生

创建一个Policeman类,继承自Person类,重写WHOAMI字段,初始化警察

添加main方法,分别创建Student/Policeman的实例,然后分别打印WHOAMI

 

代码


  
  1. // 定义一个人的抽象类
  2. abstract class Person6 {
  3.   // 没有初始化的val字段就是抽象字段
  4.   val WHO_AM_I:String
  5. }
  6. class Student6 extends Person6 {
  7.   override val WHO_AM_IString"学生"
  8. }
  9. class Policeman6 extends Person6 {
  10.   override val WHO_AM_IString"警察"
  11. }
  12. object Main6 {
  13.   def main(args: Array[String]): Unit = {
  14.     val p1 = new Student6
  15.     val p2 = new Policeman6
  16.     println(p1.WHO_AM_I)
  17.     println(p2.WHO_AM_I)
  18.   }
  19. }

 

匿名内部类

 

匿名内部类是没有名称的子类,直接用来创建实例对象。Spark的源代码中有大量使用到匿名内部类。

 

示例:

创建一个Person10抽象类,并添加一个sayHello抽象方法

添加main方法,通过创建匿名内部类的方式来实现Person10

调用匿名内部类对象的sayHello方法

 

代码:


  
  1. abstract class Person7 {
  2.   def sayHello:Unit
  3. }
  4. object Main7 {
  5.   def main(args: Array[String]): Unit = {
  6.     // 直接用new来创建一个匿名内部类对象
  7.     val p1 = new Person7 {
  8.       override def sayHelloUnit = println("我是一个匿名内部类")
  9.     }
  10.     p1.sayHello
  11.   }
  12. }

 

文章来源: lansonli.blog.csdn.net,作者:Lansonli,版权归原作者所有,如需转载,请联系作者。

原文链接:lansonli.blog.csdn.net/article/details/116615619

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。