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
class Person {
val name = "super"
var age = 18
def sayName = {
println("Person--sayName")
}
def sayHello={
println("hello")
}
}
class Student extends Person{
override
val name = "sub"
age = 20
override
def sayName = {
println("Student--sayName")
}
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
p.sayHello
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
class Person2 {
}
class Student2 extends Person2
object Test2 {
def main(args: Array[String]): Unit = {
val p: Person2 = new Student2
val s: Student2 = null
println(p.isInstanceOf[Student2])
println(p.isInstanceOf[Person2])
println(s.isInstanceOf[Student2])
println("====================")
println(p.getClass == classOf[Student2])
println(p.getClass == classOf[Person2])
println("====================")
if (p.isInstanceOf[Student2]){
val s2: Student2 = p.asInstanceOf[Student2]
println("p已经转为Student2类型")
}
}
}
- 总结
- 判断
- scala:对象.isInstanceOf[类名]
- java: 对象 instanceof 类型
- 获取
- scala: 对象.getClass 或者 calssOf[类名]
- java: 对象.getClass 或 类名.Class 或 Class.forName(“包类路径”)
- 转换
- scala: 对象.asInstanceOf[类型]
- java: (类型)对象
构造器执行顺序
- Scala中每个类都可以有一个主构造器和任意多个辅助构造器,
- 辅助构造器的第一行都必须调用其他辅助构造器或者主构造器代码;
- 子类的辅助构造器是一定不可能直接调用父类的构造器的,只能在子类的主构造器中调用父类的构造器。
- 父类的构造函数已经定义过的字段,子类在使用时,不需要使用 val/var 来修饰,否则会被认为,子类要覆盖父类的字段
package cn.hanjiaxiaozhi.extendsdemo
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
abstract class Shape {
val name:String
def getArea:Double
}
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)
}
}