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

时间:2017-07-16 22:44:08

标签: scala constructor case-class

我想在case类构造函数中声明一些辅助值,但似乎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"
        )
    }
}
即使后者更易读,也更容易维护。 (想象一下,使用比调用两种方法更复杂的操作。)为什么会这样?

有没有其他方法来编写这样的构造函数或解决此问题的方法?

3 个答案:

答案 0 :(得分:2)

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

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

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

感谢。

答案 1 :(得分:0)

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

答案 2 :(得分:0)

在第二种情况下,不允许在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定义案例类,然后在随播对象中定义apply,如下所示( for old版本可能2.11.8,必须首先定义伴侣对象,并且只有现在似乎已修复的案例类 - 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 code - https://scastie.scala-lang.org/prayagupd/yn2bJWHkQ6Gbli5Ll6I6CQ/1