实际开发中, 好多类中的内容是相似的(例如: 相似的属性和行为), 可以把这些相似的内容提取出来单独的放到一个类中(父类), 然后让那多个类(子类)和这个类(父类)产生一个关系, 从而实现子类可以访问父类的内容, 这个关系就叫: 继承.
因为scala语言是支持面向对象编程的,我们也可以使用scala来实现继承,通过继承来减少重复代码。
class/object A类 extends B类 {
..
}
叫法
需求
已知学生类(Student)和老师类(Teacher), 他们都有姓名和年龄(属性), 都要吃饭(行为), 请用所学, 模拟该需求.
//1. 定义老师类.
class Teacher{
var name = ""
var age = 0
def eat() = println("老师喝牛肉汤!...")
}
//2. 定义学生类.
class Student{
var name = ""
var age = 0
def eat() = println("学生吃牛肉!...")
}
object ClassDemo01 {
//main方法, 程序的主入口
def main(args: Array[String]): Unit = {
//3. 测试老师类.
//3.1 创建对象.
val t = new Teacher
//3.2 给属性赋值
t.name = "刘老师"
t.age = 32
//3.3 打印属性值.
println(t.name, t.age)
//3.4 调用方法
t.eat()
println("-" * 15)
//4. 测试学生类.
val s = new Student
s.name = "张三"
s.age = 21
println(s.name, s.age)
s.eat()
}
}
//1. 定义人类.
class Person {
var name = ""
var age = 0
def eat() = println("人要吃饭!...")
}
//2. 定义老师类.
class Teacher extends Person
//3. 定义学生类.
class Student extends Person
object ClassDemo02 {
def main(args: Array[String]): Unit = {
//4. 测试老师类.
val t = new Teacher
t.name = "刘老师"
t.age = 32
println(t.name, t.age)
t.eat()
println("-" * 15)
//5. 测试学生类.
val s = new Student
s.name = "张三"
s.age = 23
println(s.name, s.age)
s.eat()
}
}
在Scala中, 单例对象也是可以继承类的.
需求
定义Person类(成员变量: 姓名, 成员方法: sayHello()), 定义单例对象Student继承自Person, 然后测试.
//1. 定义Person类.
class Person {
var name = ""
def sayHello() = println("Hello, Scala!..")
}
//2. 定义单例对象Student, 继承Person.
object Student extends Person
object ClassDemo03 {
//main方法, 程序的主入口
def main(args: Array[String]): Unit = {
//3. 测试Student中的成员.
Student.name = "张三"
println(Student.name)
Student.sayHello()
}
}
子类中出现和父类一模一样的方法时, 称为方法重写. Scala代码中可以在子类中使用override来重写父类的成员,也可以使用super来引用父类的成员.
子类要重写父类中的某一个方法,该方法必须要使用override关键字来修饰
可以使用override来重写一个val字段.
注意: 父类用var修饰的变量, 子类不能重写.
使用super关键字来访问父类的成员方法
需求
定义Person类, 属性(姓名, 年龄), 有一个sayHello()方法.
然后定义Student类继承Person类, 重写Person类中的字段和方法, 并测试.
参考代码
//1. 定义父类Person.
class Person {
var name = "张三"
val age = 23
def sayHello() = println("Hello, Person!...")
}
//2. 定义子类Student, 继承Person.
class Student extends Person{
//override var name = "李四" //这样写会报错, 子类不能重写父类用var修饰的变量.
override val age = 24
override def sayHello() = {
//通过super调用父类的成员.
super.sayHello()
println("Hello, Student!...")
}
}
object ClassDemo04 {
//程序的入口.
def main(args: Array[String]): Unit = {
//3. 创建学生类型的对象, 然后测试.
val s = new Student
println(s.name, s.age)
s.sayHello()
}
}
有时候,我们设计的程序,要根据变量的类型来执行对应的逻辑, 如下图:
在scala中,如何来进行类型判断呢?
有两种方式:
概述
格式
// 判断对象是否为指定类型
val trueOrFalse:Boolean = 对象.isInstanceOf[类型]
// 将对象转换为指定类型
val 变量 = 对象.asInstanceOf[类型]
示例代码:
val trueOrFalse = p.isInstanceOf[Student]
val s = p.asInstanceOf[Student]
需求
参考代码
//1. 定义一个Person类.
class Person
//2. 定义一个Student类, 继承Person.
class Student extends Person {
def sayHello() = println("Hello, Scala!...")
}
object ClassDemo05 {
//main方法, 作为程序的主入口
def main(args: Array[String]): Unit = {
//3. 通过多态的形式创建Student类型的对象.
val p: Person = new Student
//s.sayHello() //这样写会报错, 因为多态的弊端是: 父类引用不能直接访问子类的特有成员.
//4. 判断其是否是Student类型的对象, 如果是, 将其转成Student类型的对象.
if (p.isInstanceOf[Student]) {
val s = p.asInstanceOf[Student]
//5. 调用Student#sayHello()方法
s.sayHello()
}
}
}
isInstanceOf 只能判断对象是否为指定类以及其子类的对象,而不能精确的判断出: 对象就是指定类的对象。如果要求精确地判断出对象的类型就是指定的数据类型,那么就只能使用 getClass 和 classOf 来实现.
用法
示例
示例说明
参考代码
//1. 定义一个Person类.
class Person
//2. 定义一个Student类, 继承自Person类.
class Student extends Person
object ClassDemo06 {
def main(args: Array[String]): Unit = {
//3. 创建Student类型的对象, 指定其类型为Person.
val p:Person = new Student
//4. 通过isInstanceOf关键字判断其是否是Person类型的对象.
println(p.isInstanceOf[Person]) //true,
//5. 通过isInstanceOf关键字判断其是否是Person类型的对象.
println(p.isInstanceOf[Student]) //true
//6. 通过getClass, ClassOf判断其是否是Person类型的对象.
println(p.getClass == classOf[Person]) //false
//7. 通过getClass, ClassOf判断其是否是Student类型的对象.
println(p.getClass == classOf[Student]) //true
}
}
scala语言是支持抽象类的, , 通过abstract关键字来实现.
如果类中有抽象字段或者抽象方法, 那么该类就应该是一个抽象类.
- 抽象字段: 没有初始化值的变量就是抽象字段.
- 抽象方法: 没有方法体的方法就是一个抽象方法.
// 定义抽象类
abstract class 抽象类名 {
// 定义抽象字段
val/var 抽象字段名:类型
// 定义抽象方法
def 方法名(参数:参数类型,参数:参数类型...):返回类型
}
需求
步骤
参考代码
// 创建形状抽象类
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 Circle(var radius:Double /*半径*/) extends Shape {
override def area: Double = Math.PI * radius * radius
}
object ClassDemo07 {
def main(args: Array[String]): Unit = {
val s1 = new Square(2)
val s2 = new Rectangle(2,3)
val s3 = new Circle(2)
println(s1.area)
println(s2.area)
println(s3.area)
}
}
在scala的抽象类中,不仅可以定义抽象方法, 也可以定义抽象字段。如果一个成员变量是没有初始化,我们就认为它是抽象的。
语法
abstract class 抽象类 {
val/var 抽象字段:类型
}
示例
示例说明
参考代码
//1. 定义抽象类Person, 有一个抽象字段occupation(职业)
abstract class Person {
val occupation:String
}
//2. 定义Student类继承Person, 重写抽象字段occupation.
class Student extends Person{
override val occupation: String = "学生"
}
//3. 定义Teacher类继承Person, 重写抽象字段occupation.
class Teacher extends Person{
override val occupation: String = "老师"
}
object ClassDemo08 {
//main方法, 作为程序的主入口
def main(args: Array[String]): Unit = {
//4. 创建Student类的对象, 打印occupation的值.
val s = new Student
println(s.occupation)
//5. 创建Teacher类的对象, 打印occupation的值.
val t = new Teacher
println(t.occupation)
}
}
匿名内部类是继承了类的匿名的子类对象,它可以直接用来创建实例对象。Spark的源代码中大量使用到匿名内部类。学完这个内容, 对我们查看Spark的底层源码非常有帮助.
new 类名() {
//重写类中所有的抽象内容
}
注意: 上述格式中, 如果的类的主构造器参数列表为空, 则小括号可以省略不写.
需求
参考代码
//1. 定义Person类, 里边有一个抽象方法: sayHello()
abstract class Person {
def sayHello()
}
object ClassDemo09 {
//2. 定义一个show()方法, 该方法需要传入一个Person类型的对象.
def show(p: Person) = p.sayHello()
//main方法是程序的主入口
def main(args: Array[String]): Unit = {
//3. 通过匿名内部类创建Person的子类对象, 并调用sayHello()方法.
new Person {
override def sayHello(): Unit = println("Hello, Scala, 当对成员方法仅调用一次的时候.")
}.sayHello()
//4.show直接匿名内部类
show(new Person {
override def sayHello(): Unit = println("123")
})
//4.show直接匿名内部类(函数式)(类似Java中的lambda表达式)
show(() => println("123"))
//4. 演示: 匿名内部类可以作为方法的参数进行传递.
val p = new Person {
override def sayHello(): Unit = println("Hello, Scala, 可以作为方法的实际参数进行传递")
}
show(p)
}
}