添加一个额外的case类构造函数(该函数首先转换其参数)时,我在做什么错? [英] What am I doing wrong around adding an additional case class constructor which first transforms it parameters?

查看:73
本文介绍了添加一个额外的case类构造函数(该函数首先转换其参数)时,我在做什么错?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以,我有一个非常简单的案例类:

So, I had a very simple case class:

case class StreetSecondary1(designator: String, value: Option[String])

这很好。但是,我一直在将单个字符串解析为元组的地方,然后将其用于构建此case类的实例:

This was working just fine. However, I kept having places where I was parsing a single string into a tuple which was then used to build an instance of this case class:

def parse1(values: String): StreetSecondary1 = {
  val index = values.indexOf(" ")
  StreetSecondary1.tupled(
    if (index > -1)
      //clip off string prior to space as designator and optionally use string after space as value
      (values.take(index), if (values.size > index + 1) Some(values.drop(index + 1)) else None)
    else
      //no space, so only designator could have been provided
      (values, None)
  )
}

因此,我想使用相同的解析代码将所有不同的地方重构为这种case类(但是不会编译):

So, I wanted to refactor all the different places with this same parsing code into the case class like this (but this won't compile):

case class StreetSecondary2(designator: String, value: Option[String]) {
  def this(values: String) = this.tupled(parse(values))
  private def parse(values: String): (String, Option[String]) = {
    val index = values.indexOf(" ")
    if (index > -1)
      //clip off string prior to space as designator and optionally use string after space as value
      (values.take(index), if (values.size > index + 1) Some(values.drop(index + 1)) else None)
    else
      //no space, so only designator could have been provided
      (values, None)
  }
}

似乎在添加case类构造函数并具有函数的过程中存在一些鸡/蛋问题它接受参数并在调用实际构造函数之前对其进行转换。我已经弄弄了这个(进行许多切线处理)。然后,我尝试使用伴随对象路径:

It appears there is some chicken/egg problem around adding a case class constructor AND having a function that takes the parameter(s) and transforms them prior to calling the actual constructor. I have fiddled with this (going on many tangents). I then resorted to trying the companion object pathway:

object StreetSecondary3 {
  private def parse(values: String): (String, Option[String]) = {
    val index = values.indexOf(" ")
    if (index > -1)
      //clip off string prior to space as designator and optionally use string after space as value
      (values.take(index), if (values.size > index + 1) Some(values.drop(index + 1)) else None)
    else
      //no space, so only designator could have been provided
      (values, None)
  }
  def apply(values: String): StreetSecondary3 = {
    val tuple: (String, Option[String]) = parse(values)
    StreetSecondary3(tuple._1, tuple._2)  //Why doesn't .tupled method work here?
  }
}
case class StreetSecondary3(designator: String, value: Option[String])

在StreetSecondary2中我在做什么错?有什么办法可以使其正常工作吗?当然,必须有一种更好的简单方法,无需我添加StreetSecondary3中存在的所有伴随对象样板。有吗?

What am I doing wrong in StreetSecondary2? Is there some way to get it to work? Surely there has to be a better simpler way where I am not required to add all the companion object boilerplate present in StreetSecondary3. Is there?

谢谢您的反馈和指导。

Thank you for any feedback and guidance you can give me around this.

更新

为什么!已经吸取了很多教训。

Whew! Lots of lessons learned already.

A)StreetSecondary2解析方法在构造案例类实例时不使用 this隐式上下文(即,按照Java术语是静态方法),因此更好地将其移至随播对象。

A) the StreetSecondary2 parse method does not use the "this" implicit context in the case class instance being constructed (i.e. it is a static method in Java terms), so it works better moved to the companion object.

B)不幸的是,当为案例类编写一个显式伴侣对象时,编译器提供的隐式伴侣对象丢失了。元组方法(还有其他方法,我猜-肯定希望有一种方法可以保留它并进行扩充而不是吹掉它)包含在编译器提供的隐式伴随对象中,而未包含在新的显式伴随对象中。通过将扩展((String,Option [String])=> StreetSecondary)添加到显式伴随对象中,可以解决此问题。

B) Unfortunately when composing an explicit companion object for a case class, the compiler provided "implicit companion object" is lost. The tupled method (and others, I am guessing - sure wish there was a way to keep it and augment as opposed to blowing it away) were contained in the compiler provided "implicit companion object" and not provided in the new explicit companion object. This was fixed by adding "extends ((String, Option[String]) => StreetSecondary)" to the explicit companion object.

C)这是一个更新的解决方案(它还结合了解析函数的更简洁版本,感谢加布里埃尔·彼得罗内拉(Gabriele Petronella)的感谢):

C) Here's an updated solution (which also incorporates a more terse version of the parse function with a nod of thanks to Gabriele Petronella):

object StreetSecondary4 extends ((String, Option[String]) => StreetSecondary4) {
  private def parseToTuple(values: String): (String, Option[String]) = {
    val (designator, value) = values.span(_ != ' ')
    (designator, Option(value.trim).filter(_.nonEmpty))
  }
  def apply(values: String): StreetSecondary4 =
    StreetSecondary4.tupled(parseToTuple(values))
}
case class StreetSecondary4(designator: String, value: Option[String])

在样板方面,它几乎比StreetSecondary3版本更好。但是,由于现在已经使很多隐式上下文变得显式了,所以它现在变得更加有意义。

This is barely better in terms of boilerplate than the StreetSecondary3 version. However, it now makes quite a bit more sense due to so much implicit context having now been made explicit.

推荐答案

对辅助构造函数的语言限制

You hit on a language restriction on auxiliary constructors


在Scala中,每个辅助构造函数都必须调用与第一个操作相同类的另一个构造函数。换句话说,每个Scala类中每个辅助构造函数中的第一条语句的形式均为this(。。。)。调用的构造函数可以是主构造函数(如Rational示例中的示例),也可以是在文本上位于调用构造函数之前的另一个辅助构造函数。该规则的最终结果是,Scala中的每次构造函数调用最终都会最终调用该类的主要构造函数。因此,主要的构造函数是类的单个入口点。

In Scala, every auxiliary constructor must invoke another constructor of the same class as its first action. In other words, the first statement in every auxiliary constructor in every Scala class will have the form this(. . . ). The invoked constructor is either the primary constructor (as in the Rational example), or another auxiliary constructor that comes textually before the calling constructor. The net effect of this rule is that every constructor invocation in Scala will end up eventually calling the primary constructor of the class. The primary constructor is thus the single point of entry of a class.

如果您熟悉Java,您可能会奇怪,为什么Scala的构造函数规则比Java的约束更严格。在Java中,构造函数必须首先调用同一类的另一个构造函数,或者直接调用超类的构造函数。在Scala类中,只有主构造函数可以调用超类构造函数。与Java相比,Scala中增加的限制确实是一种设计折衷,需要付出代价,以换取Scala构造函数的简洁性和简化性。

If you’re familiar with Java, you may wonder why Scala’s rules for constructors are a bit more restrictive than Java’s. In Java, a constructor must either invoke another constructor of the same class, or directly invoke a constructor of the superclass, as its first action. In a Scala class, only the primary constructor can invoke a superclass constructor. The increased restriction in Scala is really a design trade-off that needed to be paid in exchange for the greater conciseness and simplicity of Scala’s constructors compared to Java’s.

在Scala中编程,第6章,第6章第7章

因此,您不能调用 this.tupled 作为辅助构造函数的第一条语句。

Therefore you cannot call this.tupled as the first statement of your auxiliary constructor.

在这种情况下,一定要在伴随对象中添加构造函数。

Adding the constructor in the companion object is definitely the way to go in this case.

与问题本身无关,但您可以使用 span

Not relevant to the question itself, but you can greatly simplify your parse method using span

private def parse(values: String): (String, Option[String]) = {
  val (designator, value) = values.span(_ != ' ')
  (designator, Option(value.trim).filter(_.nonEmpty))
} //                              ^^^^^^^^^^^^^^^^^^
  //                              returns None if the string is empty

这篇关于添加一个额外的case类构造函数(该函数首先转换其参数)时,我在做什么错?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆