Scala构造函数参数默认为私有val吗?

138

我一直在尝试:

class Foo(bar: Int)

对比:

class Foo(private val bar: Int)

尽管我找不到任何地方说明(bar: Int)扩展为(private val bar: Int),但它们似乎表现相同,所以我的问题是,它们是否相同/相似?

另外,我一直在尝试在这些代码片段上使用-Xprint: typer,它们生成相同的代码,除了第二个中多了一行。如何理解那一行?

..
class Foo extends scala.AnyRef {
  <paramaccessor> private[this] val bar: Int = _;
  def <init>(bar: Int): this.Foo = {
    Foo.super.<init>();
    ()
  }
}
..


..
class Foo extends scala.AnyRef {
  <paramaccessor> private[this] val bar: Int = _;
  <stable> <accessor> <paramaccessor> private def bar: Int = Foo.this.bar;
  def <init>(bar: Int): this.Foo = {
    Foo.super.<init>();
    ()
  }
}
..
2个回答

187

bar: Int

这只是一个构造函数参数。如果这个变量除了构造函数以外没有被其他地方使用,它将留在那里。不会生成任何字段。否则,将创建private val bar字段,并将bar参数的值分配给它。不会创建getter。

private val bar: Int

这样声明参数会创建带有私有getter的private val bar字段。无论参数是否在构造函数之外被使用(例如在toString()中),此行为都相同。

val bar: Int

与上述相同,但类似于Scala的getter是公共的。

bar: Int在case类中

当涉及到case类时,默认情况下,每个参数都具有val修饰符。


16
如果使用类的话,所有参数都将变成“公共”的 val - drexin
1
@om-nom-nom:抱歉,我不明白。我应该改进格式/结构以使其更易读吗? - Tomasz Nurkiewicz
1
@TomaszNurkiewicz:在非case类和case类中,var可用且有意义地将构造函数参数渲染为(可变)类属性。 - Randall Schulz
10
在《Scala 快速入门》一书中,提到 bar: Int 编译成了 private[this] val bar: Int - WelcomeTo
刚刚遇到这个问题,如果你要使用反射,那么你可能需要 private val bar: Int,否则就直接使用 bar: Int - Matt Roberts
显示剩余2条评论

116
在第一种情况下,bar 只是一个构造函数参数。由于主要的构造函数是类本身的内容,因此它可以在其中访问,但仅限于此实例。所以它几乎等价于:
class Foo(private[this] val bar:Int)

另一方面,在第二种情况下,bar 是一个 普通的 私有字段,因此它可以被该实例以及Foo 的其他实例访问。 例如,以下代码可编译通过:

class Foo(private val bar: Int) {
  def otherBar(f: Foo) {
    println(f.bar) // access bar of another foo
  }
}

并运行:

scala> val a = new Foo(1)
a: Foo = Foo@7a99d0af

scala> a.otherBar(new Foo(3))
3

但是这个不行:

class Foo(bar: Int) {
  def otherBar(f: Foo) {
    println(f.bar) // error! cannot access bar of another foo
  }
}

12
这个答案比被接受的答案更好,它突出了裸的 bar: Intprivate val ... 之间的区别。 - hraban

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接