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

Posted Lansonli

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2021年大数据常用语言Scala(三十):scala面向对象 继承(extends)相关的知识,希望对你有一定的参考价值。

 

目录

继承(extends) 

简单继承

override和super

isInstanceOf和asInstanceOf

getClass和classOf

访问修饰符

调用父类的constructor

抽象类

抽象方法

抽象字段

匿名内部类


继承(extends) 

简单继承

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

示例1:实现简单继承

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:单例对象实现继承

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

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将对象转换为指定类型

 

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]可以精确获取类型
  • 使用==操作符就可以直接比较

 

示例:

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

 

代码:

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字段
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对象实例,并打印它的姓名、班级

 

代码:

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方法,分别创建正方形、长方形、圆形对象,并打印它们的面积

 

代码:

// 创建形状抽象类
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

 

代码

// 定义一个人的抽象类
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方法

 

代码:

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年大数据常用语言Scala(三十):scala面向对象 继承(extends)的主要内容,如果未能解决你的问题,请参考以下文章

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

2021年大数据常用语言Scala(三十六):scala高级用法 泛型

2021年大数据常用语言Scala(三十三):scala高级用法 模式匹配

2021年大数据常用语言Scala(三十五):scala高级用法 提取器(Extractor)

2021年大数据常用语言Scala(三十七):scala高级用法 高阶函数用法

2021年大数据常用语言Scala(三十二):scala高级用法 样例类