Scala案例类的私有构造函数并不是私有的。

10

今天我遇到了一个关于case类构造函数的奇怪问题。我想让一个构造函数私有化,但似乎这并不是问题。所以我在我的一个项目中尝试了一下,它可以工作。但在另一个项目中,我可以调用私有的构造函数,并且编译成功了。我以为是我的ide出了问题,所以我创建了一个独立的类并使用scalac进行了编译。结果也通过了。以下是代码:

package com.test

object Main {

  def main(args: Array[String]) {
    val bar = Bar("12345")
//    bar.doStuff()
    println(bar)
  }
}

case class Bar private(foo: String){
  private def doStuff():Unit = println("stuff")
}

有趣的是,如果我取消注释bar.doStuff(),它就无法编译。因此,我认为在这种情况下private是起作用的,但是对于构造函数却不起作用。我做错了什么? Scalac版本是2.11.8
2个回答

12

这个符号表示 val bar = Bar("12345")val bar = Bar.apply("12345") 的简写,也就是说,它调用了 case 类的 (自动生成的) 伴生对象apply 方法。

伴生对象可以访问私有构造函数,所以它能够正常工作。

(为什么要将 case 类的构造函数设置为 private?这听起来不像是一个好主意)。


谢谢你的回答。我知道apply方法,但在这里不知为何没有想到它。至于你的问题,我想要一个只在apply方法中初始化的字段。 - Artem Malinko
3
很可能最容易的方法是将其作为常规类(可选配伍对象)进行编写,以便您完全控制构造函数和“apply”方法。请注意,案例类只是语法糖,您可以使用常规类完成所有需要的工作(但您需要自己编写一些内容)。 - Jesper
@Jesper 是的,我最终做出了相同的决定。谢谢。 - Artem Malinko
1
让人困惑的是,它以前可是能用的:https://issues.scala-lang.org/browse/SI-9425 有关私有构造函数和复制等 case 特性的交互的票据。 - som-snytt
3
为什么要将 case class 的构造函数设为私有?如果只有在有效时才定义类,例如只有在 name 不为空且 age > 0 时才创建 Person 类,这种情况下怎么办呢?可以使用“智能构造函数”,即通过 case classobject 来实现。例如:case class Person(name: String, age: String),并使用 object Person { def build(name: String, age: Int): Option[Person] } 来创建 Person 对象。正如您所指出的,使用 case class 可以提供“合理”的 hashCodeequals 实现,从而减轻开发人员的工作负担。 - Kevin Meredith
@KevinMeredith我的评论特别针对将case class的构造函数设为私有,而不是一般情况下为什么要将构造函数设为私有。Case类具有特定的目的(作为数据传输对象,用于模式匹配),将Case类的构造函数设为私有并不符合此目的。 - Jesper

4
如果你希望Bar("abc")不能编译通过,这里有一个简单的解决方法。
在2.12.3中进行了快速测试,但在2.11.8中不起作用。
case class Bar private (value: String)
object Bar {
  private def apply(v: String) = new Bar(v) // makes the method private
  def create(value: String): Bar = ... // your own implementation
}

val bar = Bar("abc") // compile error
val bar = new Bar("abc") // compile error
val bar = Bar.create("abc") // ok

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