在 Scala 中,内部类、内部对象和匿名类是面向对象编程的重要特性,用于实现类的封装、逻辑聚合或临时类型定义。以下结合具体示例详细说明:
1. 内部类的定义
内部类是定义在另一个类(外部类)内部的类,与外部类形成逻辑上的包含关系,可直接访问外部类的成员(包括私有成员)。语法:在外部类的类体中直接定义类。
scala
class OuterClass(val outerName: String) {
// 定义内部类
class InnerClass(val innerName: String) {
// 内部类可访问外部类的成员
def printInfo(): Unit = {
println(s"外部类名称:$outerName,内部类名称:$innerName")
}
}
}
说明:
InnerClass是OuterClass的内部类,依赖于OuterClass的实例而存在。- 不同外部类实例的内部类是不同的类型(Scala 的内部类是 “路径依赖类型”,这点与 Java 不同)。
2. 内部类的基本使用
使用内部类时,需先创建外部类实例,再通过外部类实例创建内部类实例。
scala
object InnerClassDemo extends App {
// 创建外部类实例
val outer1 = new OuterClass("外部实例1")
// 通过外部实例创建内部类实例
val inner1 = new outer1.InnerClass("内部实例1")
inner1.printInfo() // 输出:外部类名称:外部实例1,内部类名称:内部实例1
// 另一个外部实例的内部类
val outer2 = new OuterClass("外部实例2")
val inner2 = new outer2.InnerClass("内部实例2")
inner2.printInfo() // 输出:外部类名称:外部实例2,内部类名称:内部实例2
// 注意:outer1.InnerClass 和 outer2.InnerClass 是不同类型
// 如下代码会编译报错(类型不兼容):
// val inner: outer1.InnerClass = inner2
}
3. 内部类的综合案例
场景:设计一个 “班级 (ClassRoom)” 类,内部包含 “学生 (Student)” 内部类,班级可管理学生,学生可访问班级信息。
scala
class ClassRoom(val className: String) {
// 存储班级内的学生
private val students = scala.collection.mutable.ListBuffer[Student]()
// 内部类:学生(依赖于具体的班级实例)
class Student(val name: String, val age: Int) {
// 学生属于当前班级,可直接访问班级名称
def getClassInfo(): String = s"学生$name 属于 $className 班"
// 学生加入班级
def join(): Unit = {
students += this // this指代当前Student实例
}
}
// 外部类方法:打印班级所有学生
def printAllStudents(): Unit = {
println(s"$className 班的学生:")
students.foreach(student => println(s"- ${student.name}(${student.age}岁)"))
}
}
// 测试
object ClassRoomDemo extends App {
val class1 = new ClassRoom("高一(1)班")
// 创建该班级的学生(内部类实例)
val stu1 = new class1.Student("张三", 16)
val stu2 = new class1.Student("李四", 17)
stu1.join()
stu2.join()
class1.printAllStudents()
// 输出:
// 高一(1)班的学生:
// - 张三(16岁)
// - 李四(17岁)
println(stu1.getClassInfo()) // 输出:学生张三 属于 高一(1)班 班
}
核心逻辑:
- 内部类
Student与外部类ClassRoom的实例绑定,确保学生属于特定班级。 - 内部类可直接操作外部类的私有成员(如
students列表),实现数据封装。
4. 内部对象(Inner Object)
内部对象是定义在类或特质内部的单例对象,常用于:
- 为外部类提供工具方法或常量。
- 实现与外部类相关的单例逻辑。
语法:在类体中用object关键字定义。
scala
class MathUtils {
// 内部对象:提供数学常量和工具方法
object Calculator {
val PI: Double = math.Pi
def add(a: Int, b: Int): Int = a + b
def multiply(a: Int, b: Int): Int = a * b
}
}
// 测试
object InnerObjectDemo extends App {
val math = new MathUtils()
println(math.Calculator.PI) // 输出:3.14159...
println(math.Calculator.add(2, 3)) // 输出:5
println(math.Calculator.multiply(2, 3)) // 输出:6
}
说明:
- 内部对象是单例,每个外部类实例共享同一个内部对象(与内部类的 “路径依赖” 不同)。
- 内部对象可访问外部类的所有成员(包括私有成员)。
5. 匿名类(Anonymous Class)
匿名类是没有名称的类,通常用于快速实现特质(Trait)或抽象类的具体逻辑,直接通过new关键字创建实例。
语法:new 特质/抽象类 { 实现抽象成员 }
示例 1:实现抽象类的匿名类
scala
// 定义抽象类
abstract class Animal {
def makeSound(): String
}
object AnonymousClassDemo extends App {
// 创建匿名类实例(直接实现抽象方法)
val dog: Animal = new Animal {
override def makeSound(): String = "汪汪"
}
val cat: Animal = new Animal {
override def makeSound(): String = "喵喵"
}
println(dog.makeSound()) // 输出:汪汪
println(cat.makeSound()) // 输出:喵喵
}
示例 2:实现特质的匿名类
scala
// 定义特质
trait Logger {
def log(message: String): Unit
}
// 使用匿名类作为参数
def processData(logger: Logger): Unit = {
logger.log("数据处理完成")
}
// 测试
processData(new Logger {
override def log(message: String): Unit = println(s"日志:$message")
})
// 输出:日志:数据处理完成
特点:
- 匿名类编译后会生成一个形如
外部类$1的类文件,但代码中无需显式命名。 - 常用于临时需要某个接口 / 抽象类的实现,避免定义单独的类文件,简化代码。
总结
- 内部类:定义在外部类内部,依赖外部类实例,可访问外部类成员,支持路径依赖类型。
- 内部对象:外部类中的单例对象,提供关联工具方法或常量,共享实例。
- 匿名类:无名称的类,直接通过
new实现抽象类 / 特质,适合临时逻辑实现。
这些特性在 Scala 中常用于封装逻辑、简化代码或实现回调机制,是面向对象设计的灵活工具。