我有一个带可选参数的函数。但是,这些不是选项[?],但可以设置为null:
private def div(id: String = null, cssClass: String = null): JQuery = {
val optId = Option(id)
val optCssClass = Option(cssClass)
...
// deal with optId and optCssClass using the Scala-way™
...
}
我正在使用“null”,我知道应该像瘟疫那样避免使用“null”。但是,它允许我编写这样的代码:
div(id = "someId") // no cssClass
div(id = "otherId", cssClass = "someClass")
我的眼睛看起来比以下更好:
div(id = Some("someId")) // no cssClass
div(id = Some("otherId"), cssClass = Some("someClass"))
这是一个已知/可接受的Scala模式吗? (使用null作为默认参数值并转换为Option)
或者它仍然是痛苦/糟糕的做法?如果是这样,为什么?
答案 0 :(得分:0)
为什么不用空字符串替换null
?
private def div(id: String = "", cssClass: String = ""): JQuery = {
val optId = if(id.isEmpty) None else Some(id)
val optCssClass = if(cssClass.isEmpty) None else Some(cssClass)
...
// deal with optId and optCssClass using the Scala-way™
...
}
然后你可以这样做:
div(id = "someId") // no cssClass
div(id = "otherId", cssClass = "someClass")
答案 1 :(得分:0)
我建议的另一种方法是构建器模式
trait ElementBuilder {
def identified(id: String): ElementBuilder
def build: JQuery
}
case class DivElement(identifier: Option[String] = None)
extends ElementBuilder {
def identified(id: String) = this.copy(identifier = Option(id))
def build: JQuery = ??? // Smth like <div id={identifier}></div>
}
val builder = DivElement()
builder.identified("foo")
val element = builder.build
此方法允许您显式设置参数,然后通过它们构建元素
答案 2 :(得分:0)
这里的大多数答案都提出了“空对象”模式的一些变体,通过将空字符串表示为“未定义”(如val optId = if(id.isEmpty) None else Some(id)
)
这里的问题是空字符串可能是有效值!对于任何字符串都是如此,尽管您可以通过使用非常令人发指的东西(可能涉及不可打印的字符)来缓解此问题。 e.g:
val UndefinedString = "THIS-IS-A-REALLY-UNLIKELY-VALID-VALUE"
private def div(
id: String = UndefinedString,
cssClass: String = UndefinedString
): JQuery = {
val optId = Option(id) filter (_ != UndefinedString )
val optCssClass = Option(cssClass) filter (_ != UndefinedString )
...
// deal with optId and optCssClass using the Scala-way™
...
}
更好的是,您可以使用其他类型来表示您的null对象。由于你不能继承String
,你必须将你的参数提升到类型层次结构并使它们成为CharSequence
s
object NullCharSeq extends CharSequence {
def charAt(idx: Int): Char = ???
def length(): Int = 0
def subSequence(start: Int, end: Int): CharSequence = this
def toString(): String = ???
}
def charSeqToOptStr(cs: CharSequence): Option[String] = cs match {
case NullCharSeq => None
case x => Option(x) map (_.toString)
}
private def div(
id: CharSequence = NullCharSeq,
cssClass: CharSequence = NullCharSeq
): JQuery = {
val optId = charSeqToOptStr(id)
val optCssClass = charSeqToOptStr(cssClass)
...
// deal with optId and optCssClass using the Scala-way™
...
}
这是一次性使用的重量级模式,但如果你经常使用它,成本会很快摊销(NullCharSeq
和charSeqToOptStr
只需要在代码库中定义一次)。
错误地传递“未定义”字符串也没有风险,就像它是一个有效值一样。另外,您可以直接接受CharBuffer / StringBuffer / StringBuilder作为参数。
答案 3 :(得分:-1)
如果这是一个明智的价值,我也会在@ Jiafeng的答案中找到一个像空字符串一样的特殊字符串。您还可以定义一个字符串,例如
val NoId = "?"
def div(id: String = NoId) = id match {
case NoId => None
case x => Some(x)
}
另一种方法是使用另一种可以从字符串或缺席中隐式创建的类型。
sealed trait MaybeId
implicit class Id(val name: String) extends MaybeId
case object NoId extends MaybeId
def div(id: MaybeId = NoId) = id match {
case NoId => None
case x: Id => Some(x.name)
}
Here是一种通用类型,其行为类似Option[A]
,具有隐式转化A => Some[A]
。