内部类

40 阅读3分钟

在 Scala 中,内部类、内部对象和匿名类是面向对象编程的重要特性,用于实现类的封装、逻辑聚合或临时类型定义。以下结合具体示例详细说明:

1. 内部类的定义

内部类是定义在另一个类(外部类)内部的类,与外部类形成逻辑上的包含关系,可直接访问外部类的成员(包括私有成员)。语法:在外部类的类体中直接定义类。

scala

class OuterClass(val outerName: String) {
  // 定义内部类
  class InnerClass(val innerName: String) {
    // 内部类可访问外部类的成员
    def printInfo(): Unit = {
      println(s"外部类名称:$outerName,内部类名称:$innerName")
    }
  }
}

说明

  • InnerClassOuterClass的内部类,依赖于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的类文件,但代码中无需显式命名。
  • 常用于临时需要某个接口 / 抽象类的实现,避免定义单独的类文件,简化代码。

总结

  1. 内部类:定义在外部类内部,依赖外部类实例,可访问外部类成员,支持路径依赖类型。
  2. 内部对象:外部类中的单例对象,提供关联工具方法或常量,共享实例。
  3. 匿名类:无名称的类,直接通过new实现抽象类 / 特质,适合临时逻辑实现。

这些特性在 Scala 中常用于封装逻辑、简化代码或实现回调机制,是面向对象设计的灵活工具。