Scala 继承
面向对象的三大特征,封装继承多态,Scala作为一款面向对象编程语言,自然不会缺少继承
继承:子类继承父类,关键字extends
, 单继承
基本使用
package hierarchy
class Animal {
var name: String = _
def run:String = name +" running..."
}
class Dog extends Animal
class Cat extends Animal
object AnimalTest{
def main(args: Array[String]): Unit = {
val dog = new Dog
val cat = new Cat
dog.name = "小黑狗"
cat.name = "小花猫"
println(dog.run)
println(cat.run)
}
}
方法覆盖
父类定义的公共方法无法满足子类需求时,在子类中定义相同的方法,调用时以子类优先;
package hierarchy
class Person {
def sleep: String = {
"sleeping..."
}
}
class SmallStudent extends Person{
// 方法覆盖 关键字override
override def sleep: String = "小学生在睡觉..."
}
class MiddleStudent extends Person
object PersonTest{
def main(args: Array[String]): Unit = {
val s1 = new SmallStudent
println(s1.sleep) // 小学生在睡觉...
val s2 = new MiddleStudent
println(s2.sleep) // sleeping...
}
}
类型检查和转换
类型检查使用:子类型.isInstanceOf[父类型]
println(s1.isInstanceOf[Person]) // true
println(s1.isInstanceOf[SmallStudent]) // true
println(s1.isInstanceOf[MiddleStudent]) // false
类型转换使用: 类型1.asInstanceOf[类型2]
// 父类型指向子类对象
val p: Person = s1.asInstanceOf[Person]
// val ms: MiddleStudent = s1.asInstanceOf[MiddleStudent]
// 将父类型强制转换为子类型
val ss = p.asInstanceOf[SmallStudent]
调用父类有参构造
结论:
- 如果调用父类的有参构造方法,需要在extends 父类
(父类的有参构造)
- 子类的主构造器中,必须有父类的成员,并且不需要声明为
val
或者var
package hierarchy
class Color(var name: String) { // 父类没有无参的主构造器,只有一个接受单个参数的主构造器
}
// 子类必须提供一个有参的主构造器,形参必须出现父类主构造器中的参数
// 只能由子类的主构造器才能调用父类的主构造器
class Red(name: String) extends Color(name: String) {
var level: String = _
def this(name: String,level:String){
this(name)
this.level = level
}
}
覆盖字段
结论:
- 如果子类需要覆盖父类中的变量(var),子类中不需要var关键字和变量的类型
- 如果子类需要覆盖父类中的常量(val),子类必须在父类常量的基础之上加上
override
关键字 - 如果父类中的私有成员(private),子类是无法覆盖,原因是:父类中属性和getter/setter方法都是私有的
package hierarchy
class Product {
// 可以覆盖
var name: String = ""
// 可以覆盖
val price: Double = 0.0
// 子类无法覆盖 属性私有 getter/setter私有
private var address: String = "made in china"
}
// 覆盖父类中的公共属性
class Telphone extends Product {
// name = "xxx"
// 父类中var变量,子类覆盖时,无需override和类型信息
name = "手机"
// 父类中val常量,子类覆盖时,必须override
override val price: Double = 10.5
}
object ProductTest {
def main(args: Array[String]): Unit = {
val telphone = new Telphone
println(telphone.name)
println(telphone.price)
}
}
抽象类
类似于java的抽象类,使用的关键字依然是
abstract
- 抽象类,可能有抽象成员和普通成员
- 子类继承抽象类,必须得实现抽象类的声明方法,如果未实现,子类依然是一个抽象类
- 抽象类只能声明引用而不能创建对象
- 在实现抽象类的抽象方式或者属性时,依然使用的是关键字
override
package hierarchy
abstract class Fruit {
// 抽象字段
var name: String
// 抽象方法
def eat(): String
}
abstract class Apple extends Fruit {
}
class Banana extends Fruit {
// 抽象属性和方法必须加override关键字
override var name: String = _
override def eat(): String = {
"eat banana ..."
}
}
object Fruit {
def main(args: Array[String]): Unit = {
val f1: Fruit = new Banana
val f2: Banana = new Banana
val f3: Fruit = new Fruit {
override var name: String = _
override def eat(): String = {
".........."
}
}
println(f1.eat()) // eat banana ..
println(f2.eat()) // eat banana ..
println(f3.eat()) // ....
}
}
访问修饰符
注意:
- java中有四种访问修饰符:public、default、protected、private
- scala中只有三种访问修饰符:default(public)、protected、private
default 访问权限最大
protected 有关系的才可以访问(伴生类,伴生对象,子类)
private 伴生类 伴生对象可以访问
package hierarchy
class A {
// 私有: 伴生类和伴生对象中方法
private var name: String = _
// 受保护限定:伴生类和伴生对象 以及子类
protected var age: Int = _
// 默认 public 所有
var sex: Boolean = _
}
class C extends A {
}
object A {
def main(args: Array[String]): Unit = {
val a = new A()
a.name
a.age
a.sex
}
}
object B {
def main(args: Array[String]): Unit = {
val a = new A
// a.name // error
a.sex
}
}
private[this]
访问修饰符的限定,限定只能在伴生类中使用,去除伴生对象的可见性
// 伴生类限定
private[this] var address: String = _
protected[this]
访问修饰符的限定,限定只能在伴生类及其子类中使用,去除伴生对象的可见性
// 伴生类和子类限定
protected[this] var phone: Long = _