1. 类的继承的定义
Scala 中的类继承是「面向对象编程」的核心特性之一,核心定义:
- 让一个类(子类 / 派生类,Subclass)复用另一个类(父类 / 基类,Superclass)的属性和方法,并可以在此基础上扩展新的功能或重写原有功能;
- 目的:代码复用(避免重复编写父类已有逻辑)、功能扩展(子类在父类基础上新增特性);
- Scala 限制:单继承(一个子类只能直接继承一个父类),但可通过「特质(Trait)」实现类似多继承的效果(后续补充)。
关键概念
- 父类:被继承的类(如
Animal); - 子类:继承父类的类(如
Dog、Cat); - 继承关系:子类 is-a 父类(
Dog is-a Animal),体现「泛化」关系; - 重写(Override):子类重新实现父类的方法 / 属性,需显式用
override关键字(Scala 强制要求,避免误重写)。
2. 继承的语法
1. 基础继承语法(子类继承父类)
// 父类定义(可含属性、方法)
class 父类名(参数列表) {
// 父类属性、方法
}
// 子类继承父类:extends 关键字
class 子类名(参数列表) extends 父类名(父类构造参数) {
// 子类新增属性、方法
// 重写父类方法/属性(需加 override)
}
2. 关键语法规则
- 继承关键字:
extends(唯一继承关键字,无implements/extends区分,特质也用extends); - 父类构造:子类构造时必须先调用父类构造(通过
extends 父类名(参数)显式传递父类构造参数); - 重写关键字:
override(重写父类非抽象方法 / 属性时必须加,重写抽象方法可省略,但推荐加); - 访问权限:父类的
private成员(属性 / 方法)子类不可访问,protected成员子类可访问,public成员(默认)所有类可访问; - 禁止继承:父类用
final修饰,则子类无法继承(如final class Animal); - 禁止重写:父类方法 / 属性用
final修饰,则子类无法重写(如final def eat(): Unit)。
3. 抽象类继承(父类为抽象类)
若父类包含未实现的抽象方法 / 属性(无方法体 / 无初始值),则父类需用 abstract 修饰,子类必须实现所有抽象成员(或子类也声明为抽象类):
// 抽象父类(abstract 修饰)
abstract class 抽象父类名 {
// 抽象属性(无初始值)
val 抽象属性名: 类型
// 抽象方法(无方法体)
def 抽象方法名(参数列表): 返回值类型
}
// 子类实现抽象类
class 子类名 extends 抽象父类名 {
// 实现抽象属性(需加 override,可省略但推荐)
override val 抽象属性名: 类型 = 初始值
// 实现抽象方法(需加 override,可省略但推荐)
override def 抽象方法名(参数列表): 返回值类型 = {
// 方法体
}
}
3. 继承的代码示例
示例 1:基础类继承(非抽象父类)
场景:定义动物父类 Animal,子类 Dog、Cat 继承 Animal,复用 name 属性和 eat() 方法,重写 sound() 方法,新增子类特有方法。
object InheritanceBasicDemo extends App {
// 父类:动物类(非抽象,含具体属性和方法)
class Animal(val name: String, val age: Int) {
// 父类具体方法(可被重写)
def eat(): Unit = {
println(s"$name 正在吃食物")
}
// 父类具体方法(子类可重写)
def sound(): Unit = {
println(s"$name 发出声音")
}
// final 方法:子类不可重写
final def sleep(): Unit = {
println(s"$name 正在睡觉")
}
}
// 子类:狗类(继承 Animal)
class Dog(name: String, age: Int, val breed: String) extends Animal(name, age) {
// 重写父类 sound() 方法(必须加 override)
override def sound(): Unit = {
println(s"小狗 $name(品种:$breed)汪汪叫")
}
// 子类特有方法(父类无)
def fetch(): Unit = {
println(s"小狗 $name 正在叼飞盘")
}
}
// 子类:猫类(继承 Animal)
class Cat(name: String, age: Int, val color: String) extends Animal(name, age) {
// 重写父类 sound() 方法
override def sound(): Unit = {
println(s"小猫 $name(颜色:$color)喵喵叫")
}
// 子类特有方法
def climbTree(): Unit = {
println(s"小猫 $name 正在爬树")
}
}
// 测试:创建子类实例
val dog = new Dog("旺财", 3, "金毛")
val cat = new Cat("咪咪", 2, "橘色")
// 调用父类继承的方法
dog.eat() // 输出:旺财 正在吃食物
cat.sleep() // 输出:咪咪 正在睡觉
// 调用重写的方法
dog.sound() // 输出:小狗 旺财(品种:金毛)汪汪叫
cat.sound() // 输出:小猫 咪咪(颜色:橘色)喵喵叫
// 调用子类特有方法
dog.fetch() // 输出:小狗 旺财 正在叼飞盘
cat.climbTree() // 输出:小猫 咪咪 正在爬树
// 访问继承的属性
println(s"狗的名字:${dog.name},年龄:${dog.age}") // 输出:狗的名字:旺财,年龄:3
}
示例 2:抽象类继承(父类为抽象类)
场景:定义抽象父类 Shape(形状),包含抽象属性 name(形状名称)和抽象方法 area()(计算面积),子类 Circle(圆形)、Rectangle(矩形)实现抽象成员。
object AbstractInheritanceDemo extends App {
// 抽象父类:形状(abstract 修饰)
abstract class Shape {
// 抽象属性:形状名称(无初始值)
val name: String
// 抽象方法:计算面积(无方法体)
def area(): Double
}
// 子类:圆形(实现 Shape)
class Circle(val radius: Double) extends Shape {
// 实现抽象属性 name(override 可选,推荐加)
override val name: String = "圆形"
// 实现抽象方法 area(override 可选,推荐加)
override def area(): Double = {
Math.PI * radius * radius // 圆面积公式:πr²
}
}
// 子类:矩形(实现 Shape)
class Rectangle(val width: Double, val height: Double) extends Shape {
override val name: String = "矩形"
override def area(): Double = {
width * height // 矩形面积公式:长×宽
}
}
// 测试:创建子类实例
val circle = new Circle(5.0)
val rectangle = new Rectangle(4.0, 6.0)
println(s"${circle.name} 的面积:${circle.area().formatted("%.2f")}") // 输出:圆形 的面积:78.54
println(s"${rectangle.name} 的面积:${rectangle.area()}") // 输出:矩形 的面积:24.0
}
示例 3:构造函数传递与访问权限
场景:演示父类构造参数传递、protected 成员访问、final 类禁止继承。
object InheritanceAdvancedDemo extends App {
// 父类:Person(含 protected 成员)
class Person(
val name: String, // public 属性(默认)
protected val age: Int // protected 属性:子类可访问,外部不可访问
) {
// protected 方法:子类可访问
protected def introduce(): String = {
s"我叫 $name,年龄 $age"
}
}
// 子类:Student(继承 Person)
class Student(name: String, age: Int, val studentId: String) extends Person(name, age) {
// 子类访问父类 protected 成员(age 和 introduce())
def studentIntroduce(): String = {
s"${introduce()},学号 $studentId"
}
}
// 测试:Student 实例
val student = new Student("张三", 20, "2025001")
println(student.name) // 输出:张三(public 可访问)
// println(student.age) // 编译报错:age 是 protected,外部不可访问
println(student.studentIntroduce()) // 输出:我叫 张三,年龄 20,学号 2025001
// final 类:禁止继承
final class FinalClass {
def sayHi(): Unit = println("我是 final 类,不可被继承")
}
// class SubFinalClass extends FinalClass {} // 编译报错:FinalClass 是 final,无法继承
}
4. 继承与多态
. 多态的定义
多态(Polymorphism) 是面向对象的核心特性,指「同一行为在不同对象上表现出不同的实现」:
- 前提:存在继承关系(子类继承父类);
- 核心:子类重写父类方法;
- 表现:父类引用指向子类对象,调用方法时实际执行的是子类的重写实现(而非父类方法)。
Scala 中多态的实现方式:
- 「子类型多态」(最常用):基于继承和重写,如父类
Animal引用指向Dog/Cat实例; - 「特质多态」:基于特质(Trait)的混入,类似多继承的多态;
- 「参数多态」:基于泛型(如
List[T]),与继承无关。
2. 多态的语法与示例
核心语法
// 父类引用指向子类对象(多态的关键)
val 父类变量名: 父类类型 = new 子类类型(参数)
// 调用方法时,实际执行子类的重写实现
父类变量名.方法名()
代码示例(基于前面的 Animal 类)
object PolymorphismDemo extends App {
// 父类:Animal(同示例 1)
class Animal(val name: String) {
def sound(): Unit = println(s"$name 发出声音")
}
// 子类:Dog(重写 sound)
class Dog(name: String) extends Animal(name) {
override def sound(): Unit = println(s"小狗 $name 汪汪叫")
}
// 子类:Cat(重写 sound)
class Cat(name: String) extends Animal(name) {
override def sound(): Unit = println(s"小猫 $name 喵喵叫")
}
// 子类:Bird(重写 sound)
class Bird(name: String) extends Animal(name) {
override def sound(): Unit = println(s"小鸟 $name 叽叽叫")
}
// 多态核心:父类引用指向不同子类对象
val animal1: Animal = new Dog("旺财")
val animal2: Animal = new Cat("咪咪")
val animal3: Animal = new Bird("啾啾")
// 调用方法:实际执行子类的重写实现(多态表现)
animal1.sound() // 输出:小狗 旺财 汪汪叫(执行 Dog 的 sound)
animal2.sound() // 输出:小猫 咪咪 喵喵叫(执行 Cat 的 sound)
animal3.sound() // 输出:小鸟 啾啾 叽叽叫(执行 Bird 的 sound)
// 批量处理:利用多态简化代码(核心优势)
def makeSound(animals: Animal*): Unit = {
animals.foreach(_.sound()) // 遍历所有动物,统一调用 sound 方法
}
println("\n批量调用动物叫声:")
makeSound(animal1, animal2, animal3)
// 输出:
// 小狗 旺财 汪汪叫
// 小猫 咪咪 喵喵叫
// 小鸟 啾啾 叽叽叫
}
3. 多态的核心优势
- 「代码复用与扩展」:批量处理不同子类对象时,无需针对每个子类写单独逻辑(如
makeSound方法可处理所有Animal子类); - 「松耦合」:父类定义统一接口,子类实现具体逻辑,新增子类时无需修改原有代码(符合「开闭原则」);
- 「灵活性」:同一父类引用可动态指向不同子类对象,执行不同逻辑。
4. 多态的限制:父类引用无法访问子类特有方法
父类引用指向子类对象时,只能调用父类中定义的方法 / 属性,无法直接访问子类特有方法(需通过「类型转换」实现):
val animal: Animal = new Dog("旺财")
animal.sound() // 正常:父类有 sound 方法
// animal.fetch() // 编译报错:Animal 类无 fetch 方法(子类特有)
// 类型转换:将父类引用转为子类类型(需确保实际是子类对象,否则抛异常)
if (animal.isInstanceOf[Dog]) { // 判断类型
val dog = animal.asInstanceOf[Dog] // 强制转换
dog.fetch() // 正常:转换后可访问子类特有方法
}
5. 特质多态(补充)
Scala 不支持多继承,但可通过「特质(Trait)」实现类似多继承的多态(一个类可混入多个特质):
object TraitPolymorphismDemo extends App {
// 特质 1:可飞
trait Flyable {
def fly(): Unit
}
// 特质 2:可游泳
trait Swimmable {
def swim(): Unit
}
// 类:鸭子(继承 Animal 类,混入 Flyable 和 Swimmable 特质)
class Duck(name: String) extends Animal(name) with Flyable with Swimmable {
override def sound(): Unit = println(s"鸭子 $name 嘎嘎叫")
override def fly(): Unit = println(s"鸭子 $name 正在飞")
override def swim(): Unit = println(s"鸭子 $name 正在游")
}
// 多态:特质引用指向实现类对象
val flyable: Flyable = new Duck("唐老鸭")
flyable.fly() // 输出:鸭子 唐老鸭 正在飞
val swimmable: Swimmable = new Duck("唐老鸭")
swimmable.swim() // 输出:鸭子 唐老鸭 正在游
val animal: Animal = new Duck("唐老鸭")
animal.sound() // 输出:鸭子 唐老鸭 嘎嘎叫
}
总结
继承核心要点
- 单继承:一个子类只能直接继承一个父类,特质可多混入;
- 构造规则:子类必须先调用父类构造,通过
extends 父类(参数)传递; - 重写规则:
override关键字必填(非抽象成员),final禁止继承 / 重写; - 访问权限:
private(父类私有)→protected(子类可访问)→public(默认,全访问)。
多态核心要点
- 前提:继承 + 子类重写父类方法;
- 表现:父类引用指向子类对象,调用方法时执行子类实现;
- 优势:代码复用、松耦合、灵活扩展;
- 限制:父类引用无法直接访问子类特有方法,需类型转换。
Scala 的继承与多态既保留了面向对象的核心特性,又通过「特质」和「函数式编程」补充了灵活性,是构建复杂系统的基础。