今天学习了Scala面向对象的继承,和Java还是有不同的地方,在此总结一下!
一.Scala中,让子类继承父类,与Java中一样,使用extends关键字。
继承就代表,子类可以从父类继承父类的field和method;然后子类可以在自己内部放入父类所没有,子类特有的field和method,使用继承可以有效的复用代码。
父类如果用final关键词修饰,field和method用final修饰,则该类是无法被继承的,field和method是无法被覆盖的。
class Person{
private var name = "xiaoming"
def getName = name
}
class Student extends Person{
private var score = 30
def getScore = score
}
执行:
val s = new Student
s.getName
输出:
xiaoming
二.override和super
Scala中,如果子类覆盖一个父类中非抽象方法,则必须使用override关键字。
在子类覆盖父类的方法之后,如果我们在子类中就是要调用父类的被覆盖的方法,可以使用super关键字,显式的调用父类的方法。
1.override method
class Person{
private var name = "xiaoming"
def getName = name
}
class Student extends Person{
private var score = 30
def getScore = score
override def getName = "Hi,my name is " + super.getName
}
执行:
val s = new Student
s.getName
输出:
Hi,my name is xiaoming
2.override field
Scala中,子类可以覆盖父类的val field,而且子类的val field还可以覆盖父类的val field的getter方法。
class Person{
val name:String = "people"
def age:Int = 0
}
class Student extends Person{
override val name:String = "xiaoming"
override def age:Int = 30
}
执行:
val s = new Student
s.name
输出:
xiaoming
三.isInstanceOf和asInstanceOf
如果我们创建了子类的对象,但是又将其赋予了父类类型的变量。则在后续的程序中,我们又需要将父类类型的变量转换为子类类型的变量,该怎么做?
首先,我们需要使用isInstanceOf判断对象是否是指定类的对象,如果是的话,则可以使用asInstanceOf将对象转换为指定类型。
注意:如果对象是null,则isInstanceOf一定返回false,asInstanceOf一定返回null。
class Person
class Student extends Person{
val p:Person = new Student //将子类对象赋给父类变量
var s:Student = null
if(p.isInstanceOf[Student]) p.asInstance[Student]
}
四.getClass和classOf
isInstanceOf只能判断出对象是否是指定类以及其子类的对象,而不能精确判断出,对象就是指定类的对象。
对象.getClass可以精确的获取对象的类,classOf[类]可以精确获取类,然后使用 == 操作符即可判断。
scala>class Person
defined class Person
scala>class Student extends Person
defined class Student
scala>val p:Person = new Student
p:Person = Student
scala>p.isInstanceOf[Person]
Boolean = true
scala>p.getClass == classOf[Person]
Boolean = false
scala>p.getClass == classOf[Student]
Boolean = true
五.使用模式匹配进行类型判断
在实际开发中,大多数情况下都使用模式匹配的方式来进行类型的判断,这种方式更加的简洁明了,而且代码可维护性和可扩张性也非常高。
使用模式匹配,功能性上来说,与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") //_是占位符
}
六.protected
跟Java一样,Scala中同样使用protected关键字修饰field和method,这样在子类中就不需要super关键字,直接就可以访问field和method。
还可以使用protected[this],则只能在当前子类对象中访问父类的field和method,无法通过其他子类对象访问父类的field和method。
class Person{
protected var name:String = "leo"
protected[this] var hobby:String = "game"
}
class Student extends Person{
def sayHello = println("Hello, " + name) //不需要super关键字就可以访问父类中的name
def makeFriends(s:Student){ //传入一个其他子类的对象
println("my hobby is " + hobby + ",your hobby is " + **s.hobby**) //程序报错,无法通过其他子类对象访问父类中的hobby属性
}
}
七.调用父类的构造函数
Scala中,每个类可以有一个主构造函数和任意多个辅助构造函数,而每个辅助构造函数的第一行都必须是调用其他辅助构造函数或者是主构造函数,因此子类的辅助构造函数是一定不能直接调用父类的构造函数的。
只能在子类的主构造函数中调用父类的构造函数。
**注意:**如果是父类中接收的参数,比如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){ //辅助构造函数,只接收一个参数name
this(name,0,0) //辅助构造函数必须首先调用主构造函数
}
def this(age:Int){ //另外一个辅助构造函数,只接收一个参数age
this("xiaoming",age,0)
}
}
八.匿名子类 (相当于Java中的匿名内部类)
在Scala中,匿名子类很强大。匿名子类,就是说,可以定义一个类的没有名称的子类,并直接创建其对象,然后将对象的引用赋予一个变量,之后甚至可以将该匿名子类的对象传递给其他函数。
class Person(protected val name:String){ //name用protected修饰,子类可以直接使用
def sayHello = "Hello,i'm " + name
}
val p = new Person("xiaoming"){ //创建匿名子类对象,并将其赋给变量p
override def sayHello = "Hi,i'm " + name //覆盖父类的方法
}
def greeting(p:Person{def sayHello:String}){
println(p.sayHello)
}
九.抽象类
如果在父类中,有某些方法无法立即实现,而需要依赖不同的子类来覆盖,重写实现自己不同的方法实现,此时可以将父类中的这些方法不给出具体的实现,只有方法签名,这种方法就是抽象方法。
而一个类中如果有一个抽象方法,那么类就必须用abstract来声明为抽象类,此时抽象类是不可以实例化的。
在子类中覆盖抽象类的抽象方法时,不需要使用override关键字。
abstract class Person(val name:String){
def sayHello:Unit
}
class Student(name:String) extends Person(name){
def sayHello:Unit = println("Hello " + name)
}