仅在满足条件时覆盖默认参数

时间:2014-03-23 20:23:39

标签: scala case-class

鉴于

case class Foo (
  x: Int = 1,
  y: String,
)

实例化所述类的最佳方法是什么,只有在满足局部条件时才覆盖默认参数(例如,对应于构造函数参数的局部变量不是None)

object Test {
  /* Let's pretend I cannot know the state of x,y
   * because they come from the network, a file... */
  val x: Option[Int] = getX()
  val y: Option[String] = getY()

  Foo(
    x=???,   // how to get x=if(x.isDefined) x else "use default of Foo" here
    y=y.get,
  )
}

检查条件和以不同方式实例化案例类的直接解决方案不会缩放(具有默认值的七个参数 - > 128个不同的实例化)

解决方案1)我知道的一个解决方案是:

object Foo {
  val Defaults = Foo(y="")
}

object Test {
  val x: Option[Int] = getX()
  val y: Option[String] = getY()

  Foo(
    x=x.getOrElse(Foo.Defaults.x)
    y=y.get
  )
}

这很好用。当y为None时,我得到NoSuchElementException,但这没关系,因为它是一个必需的构造函数参数。然而,这显然是一个黑客,并且有一个明显的缺点,即可以写:

  Foo(
    x=x.getOrElse(Foo.Defaults.x)
    y=y.getOrElse(Foo.Defaults.y)
  )

当y为None时,您会得到y的非感性默认值。

解决方案2)另一种解决方案如下:

sealed trait Field
case object X extends Field

object Foo {
  private val template = Foo(y="")
  val Defaults = {
    case X => template.x
  }
}

object Test {
  val x: Option[Int] = getX()
  val y: Option[String] = getY()

  Foo(
    x=x.getOrElse(Foo.Defaults(X))
    y=y.get
  )
}

这在安全性方面要好一些,但现在我必须为每个默认参数创建一个类型。

正确简洁的解决方案是什么样的?

1 个答案:

答案 0 :(得分:1)

我不清楚你如何做得比以下更好:

object Foo { def apply(optX: Option[Int], optY: Option[String]): Foo = Foo(optX.getOrElse(1), optY.get) }
case class Foo private(x: Int, y: String)

Foo(Some(5), None) // fails with NoSuchElementException
Foo(Some(5), Some("Hi")) // succeeds in creating Foo(5, "Hi")
Foo(None, Some("Hi")) // succeeds in creating Foo(1, "Hi"), note the default value of x

参数是必需的还是带有默认值的可选参数是通过前者get的调用和后者getOrElse的调用来编码的。

当然,您可以包含Option的{​​{1}}方法,以便在省略所需参数时提供更有意义的消息。

我意识到距离您的解决方案1并不远,可能无法满足您的需求。