1

来自 java 背景,我总是将实例变量标记为私有。我正在学习 scala,几乎所有我查看过的 val/var 实例的代码都具有默认(公共)访问权限。为什么这是访问?它不会破坏信息隐藏/封装原则吗?

4

4 回答 4

7

它将帮助您指定哪些代码,但请记住,某些示例代码采用简化形式,以突出显示该示例应该向您展示的内容。由于默认访问是公开的,这意味着为了简单起见,您经常会忽略修饰符。

也就是说,由于 a是不可变的,只要您认识到它现在是您的 class 的 API 的一部分,val将其公开并没有太大的危害。这完全可以:

class DataThingy(data: Array[Double) {
  val sum = data.sum
}

或者它可以是您不应该公开的实现细节:

class Statistics(data: Array[Double]) {
  val sum = data.sum
  val sumOfSquares = data.map(x => x*x).sum
  val expectationSquared = (sum * sum)/(data.length*data.length)
  val expectationOfSquare = sumOfSquares/data.length 
  val varianceOfSample = expectationOfSquare - expectationSquared
  val standardDeviation = math.sqrt(data.length*varianceOfSample/(data.length-1))
}

在这里,我们在课堂上散布了计算标准差的所有中间步骤。这尤其愚蠢,因为这不是用浮点数计算标准差的最稳定的数值方法。

如果可能的话,最好使用本地块或private[this]定义来执行中间计算,而不是仅仅将所有这些私有化:

val sum = data.sum
val standardDeviation = {
  val sumOfSquares = ...
  ...
  math.sqrt(...)
}

或者

val sum = data.sum
private[this] def findSdFromSquares(s: Double, ssq: Double) = { ... }
val standardDeviation = findMySD(sum, data.map(x => x*x).sum)

如果您需要存储计算以供以后使用,那么private valorprivate[this] val是要走的路,但如果它只是计算的中间步骤,则上面的选项更好。

var同样,如果 a 是接口的一部分(例如可变向量上的向量坐标),则暴露它也没有害处。但是当它是一个实现细节时,你应该制作它们private(更好:private[this]如果可以的话!)。

于 2013-01-04T16:58:50.900 回答
3

Java 和 Scala 之间的一个重要区别是,在 Java 中,您不能在不破坏源代码和二进制兼容性的情况下用 getter 和 setter 方法替换公共变量(反之亦然)。在 Scala 中你可以。

所以在Java中,如果你有一个公共变量,它是一个变量的事实将暴露给用户,如果你改变它,用户必须改变他的代码。在 Scala 中,您可以用vargetter 和 setter 方法(或val仅用 getter 方法的 public)替换 public,而用户不知道它们之间的区别。所以从这个意义上说,没有公开实现细节。


例如,让我们考虑一个矩形类:

class Rectangle(val width: Int, val height:Int) {
  val area = width * height
}

现在,如果我们后来决定不希望将区域存储为变量,而是应该在每次调用时计算它,会发生什么?

在 Java 中情况是这样的:如果我们使用了一个 getter 方法和一个私有变量,我们可以只删除变量并更改 getter 方法来计算面积,而不是使用变量。无需更改用户代码。但是由于我们使用了公共变量,我们现在被迫破坏用户代码:-(

在 Scala 中就不同了:我们可以改变valto就可以def了。无需更改用户代码。

于 2013-01-04T17:20:36.120 回答
2

实际上,一些 Scala 开发人员倾向于过多地使用默认访问。但是您可以在著名的 Scala 项目中找到合适的示例(例如,Twitter 的 Finagle)。

另一方面,将对象创建为不可变值是 Scala 的标准方式。如果它们是完全不可变的,我们不需要隐藏所有属性。

于 2013-01-04T16:47:26.230 回答
2

我想用更通用的方法来回答这个问题。我认为您正在寻找的答案与构建 Scala 的设计范例有关。与您在 Java 中看到的经典过程/面向对象方法不同,函数式编程被用于更高的扩展。当然,我无法涵盖您提到的所有代码,但总的来说(编写良好的)Scala 代码不需要很多可变性。

正如 Rex 所指出的,val's 是不可变的,所以没有什么理由让它们不公开。但在我看来,不变性本身并不是一个目标,而是函数式编程的结果。因此,如果我们将函数视为类似部件的东西,x -> function -> y那么function它就变成了一个黑盒子;我们并不真正关心它做了什么,只要它正确地做。正如Haskell Wiki所写:

纯函数式程序通常对不可变数据进行操作。不是更改现有值,而是创建更改的副本并保留原始值。

这也解释了缺少闭包的原因,因为我们传统上想要隐藏的部分是在函数中执行的,因此无论如何都是隐藏的。

所以,简而言之,我认为可变性和封闭性在 Scala 中变得更加多余。既然可以避免,为什么还要用 getter 和 setter 把事情搞得一团糟呢?

于 2013-01-04T17:25:26.587 回答