继承(extends)
简单继承
scala和Java一样,使用extends关键字来实现继承。可以在子类中定义父类中没有的字段和方法,或者重写父类的方法。
示例1:实现简单继承
代码语言:javascript复制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:单例对象实现继承
代码语言:javascript复制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
代码语言:javascript复制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]可以精确获取类型
- 使用==操作符就可以直接比较
示例:
代码语言:javascript复制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字段
代码:
代码语言:javascript复制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对象实例,并打印它的姓名、班级
代码:
代码语言:javascript复制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方法,分别创建正方形、长方形、圆形对象,并打印它们的面积
代码:
代码语言:javascript复制// 创建形状抽象类
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
代码
代码语言:javascript复制// 定义一个人的抽象类
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方法
代码:
代码语言:javascript复制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
}
}