Scala:为什么我不能在构造函数中声明值?

Scala: why can't I declare values in a constructor?

我想在 case class 构造函数中声明一些辅助值,但 Scala 似乎不正确。

总之,下面这段代码是正确的:

case class Something(
    text1: String,
    text2: String
) {
    def this(datetime: LocalDateTime) {
        this(
            s"date: ${datetime.toLocalDate.toString()}",
            s"time: ${datetime.toLocalTime.toString()}"
        )
    }
}

下面不是:

case class Something(
    text1: String,
    text2: String
) {
    def this(datetime: LocalDateTime) {
        val date = datetime.toLocalDate.toString()
        val time = datetime.toLocalTime.toString()
        this(
            s"date: $date",
            s"time: $time"
        )
    }
}

尽管后者会更易读且更易于维护。 (想象一下使用比调用两个方法更复杂的操作。)这是为什么?

是否有另一种方法来编写这样的构造函数或解决此问题的方法?

辅助构造函数有一个约束,它应该在其主体的第一行调用先前的辅助构造函数或主构造函数。第二个代码不遵循该规则。因此错误。

在第二种情况下,您不允许在 this(params) 调用之前在构造函数中定义变量,如 computing inside constructors are discouraged in scala class or case class解决它的一种方法是传递内联构造函数参数。

  test("test case class custom constructor") {
    case class Something(text1: String,text2: String) {

      def this(datetime: LocalDateTime) {
        this(datetime.toLocalDate.toString(), datetime.toLocalTime.toString())
        //you can do whatever you want after this(x, y) is invoked
        val testVal = "apple"
        println(testVal)
      }
    }

    new Something(LocalDateTime.now()).text1 shouldBe "2017-07-16"
    new Something(LocalDateTime.now()).text2 should not be empty
  }

另一种方式(Encouraged way) is define case class and then define apply inside a companion object as below (for older version maybe 2.11.8, companion object had to be defined first and only case class which seems to be fixed now - https://issues.scala-lang.org/browse/SI-3772)

  test("test class with companion apply method") {

    case class Something(val text1: String, val text2: String) {}

    object Something {

      def apply(datetime: LocalDateTime): Something = {

        val x = datetime.toLocalDate.toString()
        val y = datetime.toLocalTime.toString()

        new Something(x, y)
      }
    }

    Something(LocalDateTime.now()).text1 shouldBe "2017-07-16"
    Something(LocalDateTime.now()).text2 should not be empty

  }

scastie 代码 - https://scastie.scala-lang.org/prayagupd/yn2bJWHkQ6Gbli5Ll6I6CQ/1

在 Scala 中,第一次调用必须是主构造函数。之后,您可以拥有任意多的代码。阅读 this 以获得解释。

类似的规则适用于 Java 和 super。虽然不完全一样。阅读this

this 和 super 必须放在第一个的原因是,可以在调用实际的 this(x, y) 之前将字段设置为各种值。这意味着正在构造对象,并且在构造过程中可能引用该对象的任何线程都可以看到不同的值。

谢谢。