前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >2021年大数据常用语言Scala(三十):scala面向对象 继承(extends)

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

作者头像
Lansonli
发布2021-10-11 15:35:54
4990
发布2021-10-11 15:35:54
举报
文章被收录于专栏:Lansonli技术博客Lansonli技术博客

继承(extends) 

简单继承

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

示例1:实现简单继承

代码语言:javascript
复制
class Person {
  var name = "super"

  def getName = this.name
}

class Student extends Person

object Main13 {
  def main(args: Array[String]): Unit = {
    val p1 = new Person()
    val p2 = new Student()

    p2.name = "张三"

    println(p2.getName)
  }
}

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

代码语言:javascript
复制
class Person {
  var name = "super"

  def getName = this.name
}

object Student extends Person

object Main13 {
  def main(args: Array[String]): Unit = {

    println(Student.getName)
  }
}

override和super

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

示例1:class继承class

代码语言:javascript
复制
class Person {
  val name = "super"

  def getName = name
}

class Student extends Person {
  // 重写val字段
  override val name: String = "child"

  // 重写getName方法
  override def getName: String = "hello, " + super.getName
}

object Main13 {
  def main(args: Array[String]): Unit = {
    println(new Student().getName)
  }
}

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

isInstanceOf和asInstanceOf

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

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

  • isInstanceOf判断对象是否为指定类的对象
  • asInstanceOf将对象转换为指定类型
代码语言:javascript
复制
class Person3
class Student3 extends Person3

object Main3 {
  def main(args: Array[String]): Unit = {
    val s1:Person3 = new Student3

    // 判断s1是否为Student3类型
    if(s1.isInstanceOf[Student3]) {
      // 将s1转换为Student3类型
      val s2 =  s1.asInstanceOf[Student3]
      println(s2)
    }

  }
}

getClass和classOf

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

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

示例:

代码语言:javascript
复制
class Person4
class Student4 extends Person4

object Student4{
  def main(args: Array[String]) {
    val p:Person4=new Student4
    //判断p是否为Person4类的实例
    println(p.isInstanceOf[Person4])//true

    //判断p的类型是否为Person4类
    println(p.getClass == classOf[Person4])//false

    //判断p的类型是否为Student4类
    println(p.getClass == classOf[Student4])//true
  }
}

访问修饰符

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字段

代码:

代码语言:javascript
复制
class Person {
  // 只有在当前对象中能够访问
  private[this] var name = "super"

  def getName = this.name // 正确!

  def sayHelloTo(p:Person) = {
    println("hello" + p.name)     // 报错!无法访问
  }
}

object Person {
  def showName(p:Person) = println(p.name)  // 报错!无法访问
}

protected[this]修饰符

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

示例:

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

创建一个Student类

    • 添加一个showName方法,在方法中访问name字段
    • 添加一个sayHelloTo2方法,接收一个Person类型参数,在方法中打印该参数的name字段
代码语言:javascript
复制
class Person {
  // 只有在当前对象以及继承该类的当前对象中能够访问
  protected[this] var name = "super"
  
  def getName = {
    // 正确!
    this.name
  }

  def sayHelloTo1(p:Person) = {
    // 编译错误!无法访问
    println(p.name)
  }
}

object Person {
  def sayHelloTo3(p:Person) = {
    // 编译错误!无法访问
    println(p.name)
  }
}

class Student extends Person {
  def showName = {
    // 正确!
    println(name)
  }

  def sayHelloTo2(p:Person) = {
    // 编译错误!无法访问
    println(p.name)
  }
}

调用父类的constructor

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

步骤:

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

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

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

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

代码:

代码语言:javascript
复制
class Person5(var name:String)
// 直接在父类的类名后面调用父类构造器
class Student5(name:String, var clazz:String) extends Person5(name)

object Main5 {
  def main(args: Array[String]): Unit = {
    val s1 = new Student5("张三", "三年二班")
    println(s"${s1.name} - ${s1.clazz}")
  }
}

抽象类

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

不完整定义有两种情况:

方法没有方法体

变量没有初始化

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

抽象方法

示例1:

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

步骤:

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

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

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

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

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

代码:

代码语言:javascript
复制
// 创建形状抽象类
abstract class Shape {
  def area:Double
}

// 创建正方形类
class Square(var edge:Double /*边长*/) extends Shape {
  // 实现父类计算面积的方法
  override def area: Double = edge * edge
}

// 创建长方形类
class Rectangle(var length:Double /*长*/, var width:Double /*宽*/) extends Shape {
  override def area: Double = length * width
}

// 创建圆形类
class Cirle(var radius:Double /*半径*/) extends Shape {
  override def area: Double = Math.PI * radius * radius
}

object Main6 {
  def main(args: Array[String]): Unit = {
    val s1:Shape = new Square(2)
    val s2:Shape = new Rectangle(2,3)
    val s3:Shape = new Cirle(2)

    println(s1.area)
    println(s2.area)
    println(s3.area)
  }
}

抽象字段

示例2:

步骤:

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

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

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

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

代码

代码语言:javascript
复制
// 定义一个人的抽象类
abstract class Person6 {
  // 没有初始化的val字段就是抽象字段
  val WHO_AM_I:String
}

class Student6 extends Person6 {
  override val WHO_AM_I: String = "学生"
}

class Policeman6 extends Person6 {
  override val WHO_AM_I: String = "警察"
}

object Main6 {
  def main(args: Array[String]): Unit = {
    val p1 = new Student6
    val p2 = new Policeman6

    println(p1.WHO_AM_I)
    println(p2.WHO_AM_I)
  }
}

匿名内部类

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

示例:

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

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

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

代码:

代码语言:javascript
复制
abstract class Person7 {
  def sayHello:Unit
}

object Main7 {
  def main(args: Array[String]): Unit = {
    // 直接用new来创建一个匿名内部类对象
    val p1 = new Person7 {
      override def sayHello: Unit = println("我是一个匿名内部类")
    }
    p1.sayHello
  }
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-05-10 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 继承(extends) 
    • 简单继承
      • override和super
        • isInstanceOf和asInstanceOf
          • getClass和classOf
            • 访问修饰符
              • 调用父类的constructor
                • 抽象类
                  • 抽象方法
                  • 抽象字段
                • 匿名内部类
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档