scala-面向对象编程之继承

extends

scala中,让子类继承父类,与java一样,也是使用extends关键字。

继承就代表子类可以从父类继承父类的field和method;然后子类可以在自己内部放入父类所没有,子类特有的field和method;使用继承可以有效复用代码。

子类可以覆盖父类的field和method;但如果父类用final修饰,field和method用final修饰,则该类是无法被继承的,field和method是无法被覆盖的。

class Person {
private var name = "wei"
def getName = name
}

class Student extends Person {
private var score = "A"
def getScore = score
}

scala> val s = new Student
s: Student = Student@f9ac719

scala> s.getName
res6: String = wei

scala> s.getScore
res7: String = A

override 和 super

scala中,如果子类要覆盖父类中的非抽象方法,则必须使用override关键字。override关键字可以帮助我们尽早的发现代码里的错误,比如:override修饰的父类方法的方法名我们拼写错了;比如要覆盖的父类方法的参数我们写错了等待。

此外,在子类覆盖父类的方法之后,如果我们在子类中就是要调用父类的被覆盖的方法呢?那就可以使用super关键字,显示地指定要调用的父类方法。

class Person {
private var name = "wei"
def getName = name
}
class Student extends Person {
private var score = "A"
def getScore = score
override def getName = "Hi ,I'm" + super.getName
}

// Exiting paste mode, now interpreting.

defined class Person
defined class Student

scala> var s = new Student
s: Student = Student@632a7680

scala> s.getName
res8: String = Hi ,I'mwei

override field

scala中,子类可以覆盖父类的val field,而且子类的val field可以覆盖父类的val field的getter方法;只要在子类中使用override关键字即可

class Person {
val name: String = "person"
def age: Int = 0
}

class Student extends Person {
override val name: String = "leo"
override val age: Int = 30
}

scala> val p = new Person
p: Person = Person@2bac4423

scala> p.name
res9: String = person

scala> p.age
res10: Int = 0

scala> val s = new Student
s: Student = Student@7c87c960

scala> s.name
res11: String = leo

scala> s.age
res12: Int = 30

isInstanceOf 和 asInstanceOf

如果我们创建子类的对象,但是又将其赋予父类类型的变量,则在后续的程序中,我们又需要将父类类型的变量转换为子类类型的变量,应该如何做?

首先用isInstanceOf 判断对象是否是指定类型的对象,如果是的话,可以使用asInstanceOf将对象转化为指定类型。

注意,如果对象是null,则isInstanceOf一定返回false, asInstanceOf 一定返回null
注意,如果没有用isInstanceOf先判断对象是否为指定类的实例,就直接使用asInstanceOf转换,则可能会抛出异常。

class Person
class Student extends Person

scala> val p:Person = new Student
p: Person = Student@45571cc9

scala> var s:Student = null
s: Student = null

scala> if (p.isInstanceOf[Student]) s = p.asInstanceOf[Student]

getClass 和 classOf

isInstanceOf只能判断出对象是否是指定类以及其子类对象,而不能精确判断出,对象就是指定类的对象。

如果要精准的判断对象就是指定类的对象,那么就只能使用getClass 和 classOf了

getClass 可以精准获取对象的类,classOf 可以准确获取类,然后用==符即可判断

class Person
class Student extends Person
val p:Person  = new Student

scala> p.isInstanceOf[Person]
res15: Boolean = true

scala> p.getClass == classOf[Person]
res16: Boolean = false

scala> p.getClass == classOf[Student]
res17: Boolean = true

使用模式匹配进行类型判断

在实际开发中,比如spark的源码中,大量地方都是使用模式匹配的方式进行类型判断,这种方式更加地简洁明了,而且代码的可维护性和可扩展性也非常高

使用模式匹配,功能性上来说,与isInstanceOf一样,也是判断只要是该类以及该类的子类的对象即可,不是精准判断的

class Person
class Student extends Person

val p:Person = new Student

p match{
case per:Person => println("it's Person's object")
case _ => println("unknown type")

it's Person's object
}

protected

跟java一样,scala中同样可以使用protected关键字来修饰field和method,这样在子类中就不需要super关键字,直接就可以访问到field和method

还可以使用protected[this],则只能在当前子类对象中访问父类的field和method,无法通过其他子类对象访问父类的field和method

class Person {
protected var name:String = "wei"
protected[this] var hobby:String = "game"
}
class Student extends Person{
def sayHello = println("Hello, " + name)
def makeFriends(s:Student){
println("my hobby is " + hobby + ",your hobby is " + s.hobby)
}
}

<console>:20: error: value hobby is not a member of Student
       println("my hobby is " + hobby + ",your hobby is " + s.hobby)
                                                              

以上代码会报错,因为hobby用protected[this]修饰,无法通过其他子类对象访问。

调用父类的constructor

scala中,每个类可以有一个主constructor和任意多个辅助constructor,而每个辅助constructor的第一行都必须是调用其他辅助constructor或者是主constructor;因此子类的辅助constructor是一定不可能直接调用父类的constructor的

只能在子类的主constructor中调用父类的constructor,以下这种语法就是通过子类的主constructor调用父类的constructor

注意,如果是父类中接收的参数,比如name和age,子类中接收时,就不要用任何val或var来修饰了,否则会认为是子类要覆盖父类的field

class Person(val name:String, val age:Int)
class Student(name:String, age:Int, var score:Double) extends Person(name,age) {
def this(name:String){
this(name,0,0)
}
def this(age:Int) {
this("wei", age, 0)
}
}

val s = new Student("wei", 0, 0)

匿名内部类

在scala中,匿名子类是非常常见的,而且非常强大的,spark的源码中也大量使用了这种匿名子类。

匿名子类,也就是说,可以定义一个类的没有名称的子类,并直接创建其对象,然后将对象的引用赋予一个变量,之后甚至可以将该匿名子类的对象传递给其他函数。

class Person(protected val name:String) {
def sayHello = "Hello,I'm " + name
}
val p = new Person("wei"){
override def sayHello = "Hi,I'm " + name
}
def greeting(p:Person{def sayHello:String}) {
println(p.sayHello)
}

greeting(p)

抽象类

如果在父类中,有某些方法无法立即实现,而需要依赖不同的子类来覆盖,重写实现自己不同的方法实现。此时可以将父类中的这些方法不给出具体的实现,只有方法签名,这种方法就是抽象方法。

而一个类中如果有一个抽象方法,那么类不许用abstract来声明为抽象类,此时抽象类是不可以实例化的

在子类中覆盖抽象类的抽象方法时,不需要使用override关键字

abstract class Person(val name:String) {
def sayHello:Unit
}
class Student(name:String) extends Person(name) {
def sayHello:Unit = println("Hello," + name)
}

抽象field

如果在父类中,定义了field,但是没有给出初始值,则此field为抽象field

抽象field意味着,scala会根据自己的规则,为val或var类型的field生成对应的getter和setter方法,但是父类中是没有该field的

子类必须覆盖filed,以定义自己的具体field,并且覆盖抽象field,不需要使用override关键字

abstract class Person {
val name:String
}
class Student extends Person {
val name:String = "wei"
}
scala> val s = new  Student
s: Student = Student@22175d4f

scala> s.name
res6: String = wei

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章