为什么不将val或var变量标记为私有?

时间:2013-01-04 16:26:41

标签: scala encapsulation

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

4 个答案:

答案 0 :(得分:7)

这有助于您指定哪个代码,但请记住,某些示例代码是以简化形式突出显示该示例应该向您显示的内容。由于默认访问是公开的,这意味着为了简单起见,您经常会将修改符留在原点。

也就是说,因为val是不可变的,所以只要你认识到它现在是你的类的API的一部分,那么将它公开并没有什么害处。这完全没问题:

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] defs执行中间计算是更好的风格,而不仅仅是将所有这些都设为私有:

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 valprivate[this] val是可行的方法,但如果它只是计算的中间步骤,则上述选项会更好。< / p>

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

答案 1 :(得分:3)

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

所以在Java中,如果你有一个公共变量,它是一个变量的事实将暴露给用户,如果你改变它,用户必须改变他的代码。在Scala中,您可以使用getter和setter方法(或只使用getter方法的公共var)替换公共val,而无需用户知道差异。因此,从这个意义上讲,没有实现细节。


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

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

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

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

在Scala中它有所不同:我们可以将val更改为def,就是这样。无需更改用户代码。

答案 2 :(得分:2)

实际上,一些Scala开发人员倾向于使用默认访问权限。但是你可以在着名的Scala项目中找到合适的例子(例如,Twitter的Finagle)。

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

答案 3 :(得分:2)

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

正如雷克斯所指出的那样,val是不可改变的,所以没有理由让它们不公开。但正如我所看到的那样,不变性本身并不是一个目标,而是功能性编程的结果。因此,如果我们将函数视为类似x -> function -> y的函数,则function部分会变成某个黑盒子;我们并不关心它的作用,只要它正确地做到了。正如Haskell Wiki写道:

  

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

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

因此,为了缩短内容,我认为Scala中的可变性和封闭性变得更加冗余。为什么在可以避免的情况下,为什么要用吸气剂和制定者搞砸了呢?