1. 简介
Scala 提供了一套灵活的访问控制机制,允许我们通过不同的访问修饰符来控制类、对象、包等作用域中成员的可见性。本文将深入探讨 Scala 中的各种访问修饰符,并分析它们在类、对象和包等不同作用域下的行为。
2. 类和对象作用域
在 Scala 中,如果一个类成员是公开的(public),则无需显式声明访问修饰符。事实上,Scala 并没有 public
关键字。当我们不指定任何修饰符时,默认就是 public,其行为与 Java 中的 public 相同。
不过,**私有成员必须显式标注为 private
**:
class Rectangle(widthParam: Int, heightParam: Int, colorParam: String) {
val width: Int = widthParam
val height: Int = heightParam
val color: String = colorParam
private val numberOfVertexes: Int = 4
//...
}
✅ 在这个例子中,numberOfVertexes
是私有成员,只能在 Rectangle
类内部访问。
Scala 还提供了一种更严格的访问控制:**private[this]
**,它限制成员只能被当前实例访问:
abstract class Figure {
//...
private[this] val code = randomUUID.toString
def printCode: Unit = println(s"$code")
def compareCodes(that: Figure) = {
this.code == that.code // ❌ that.code 无法访问
}
}
⚠️ private[this]
是最严格的访问修饰符,只允许当前实例访问该字段,即使是同一个类的其他实例也无法访问。
3. 包作用域
3.1. protected
修饰符
标记为 protected
的成员,只能在定义它们的类及其子类中访问。例如,我们将抽象类 Figure
中的 color
字段标记为 protected
:
abstract class Figure {
//...
protected val color: String
}
✅ 此时,color
字段在 Figure
类及其子类之外是不可访问的,尝试在其他地方访问会引发编译错误。
3.2. protected[packageName]
修饰符
我们还可以通过 protected[packageName]
使成员在指定包及其子包中可访问。例如:
abstract class Figure {
//...
protected[accessmodifiers] val color: String
}
然后在同一个包或子包中的类可以访问该字段:
class Composition(fig1: Figure, fig2: Figure) {
val color = mixColors(fig1.color, fig2.color)
//...
}
✅ 这种方式在大型项目中非常实用,可以控制模块之间的可见性。
3.3. protected[this]
修饰符
使用 protected[this]
修饰的成员,只能在当前实例以及子类实例中访问:
abstract class Figure {
//...
protected[this] val lineWidth: Int
}
⚠️ 与其他 protected
成员不同,protected[this]
的访问范围更小,不允许访问其他实例中的该字段。
4. 嵌套类作用域
在嵌套类中,Scala 的访问控制规则与 Java 有所不同。
假设我们有一个 Rectangle
类,其中包含一个私有布尔字段 isSquare
,以及一个嵌套类 InnerFigure
:
class Rectangle(widthParam: Int, heightParam: Int, colorParam: String) extends Figure {
//...
private val isSquare = width == height
class InnerFigure(fig: Figure) {
private val codeInner = randomUUID.toString
val isInsideSquare = isSquare
def printCodeInner = print(codeInner)
}
}
✅ 在这个例子中,外层类的私有成员 isSquare
可以被嵌套类访问,这是 Scala 与 Java 的一个关键区别。
❌ 但反过来,嵌套类中的私有成员 codeInner
不能被外层类访问。
5. 伴生对象访问
Scala 中的类和伴生对象之间具有特殊的访问权限。
假设我们有一个 Star
类及其伴生对象:
class Star(val vertexes: Map[Int, (Double, Double)], val color: String) extends Figure {
import Star._
override val lineWidth: Int = 1
//...
val x = vertexes.values.toList.map(_._1)
override protected val rightMostPoint = x.max
val area = areaByVertexes(vertexes)
}
object Star {
def apply(vertexes: Map[Int, (Double, Double)], color: String) =
new Star(vertexes, color)
private def areaByVertexes(vertexes: Map[Int, (Double, Double)]): Double = ??? // implemented somehow
val right = rightMostPoint // ❌ Cannot resolve symbol rightMostPoint
}
✅ 在伴生类中通过 import Star._
可以访问伴生对象的所有成员(包括私有成员)。
❌ 但伴生对象无法访问伴生类的成员。
6. 总结
本文详细介绍了 Scala 中的各种访问修饰符,并探讨了它们在类、对象、包及嵌套类中的行为差异。与 Java 相比,Scala 提供了更灵活、更细粒度的访问控制方式,尤其在嵌套类和伴生对象的处理上,有其独特之处。
📌 所有示例代码均可在 GitHub 项目 中找到。