正如标题所述,在Scala中为可选参数建模的最佳方法是什么?
对于可选参数,我的意思是执行函数主体不需要的值。
因为该参数存在默认值,或者根本不需要参数本身(例如,配置或调试标志);请注意,在Java上,我可能会传递null
给这些参数。
这是Scala社区的常见问题解答,特别是由新手制作的。
例如:
总体上,社区对此达成共识,即以下列出的所有提议或替代方案都不值得其权衡取舍。
因此,建议的解决方案是仅使用Option数据类型,然后手动/显式将值包装在Some
def test(required: Int, optional: Option[String] = None): String =
optional.map(_ * required).getOrElse("")
test(required = 100) // ""
test(required = 3, optional = Some("Foo")) // "FooFooFoo"
但是,这种方法的明显缺点是需要随时待命的样板。但是,可以说这使代码更易于阅读和理解,从而易于维护。
不过,有时您可以使用其他技术(例如默认参数或重载)提供更好的API,如下所述。
由于先前解决方案的样板,一遍又一遍地提到了使用隐式转换的常见替代方法。例如:
implicit def a2opt[A](a: A): Option[A] = Some(a)
这样以前的函数可以这样调用:
test(required = 3, optional = "Foo")
不利的一面是隐式转换隐藏了一个事实,它optional
是一个可选参数(当然,如果它被命名为别的),并且这种转换可以应用于代码的许多其他(非预期)部分。这就是通常不鼓励隐式转换的原因。
另一个替代方案是使用扩展方法,而不是像那样的隐式转换optional = "foo".opt
。但是,扩展方法甚至需要添加更多代码,并且现场调用仍然具有一些样板,这一事实使它像一个平庸的中间点。
(免责声明,如果你使用的猫,你已经有了这样的扩展方法的范围.some
,所以你可能需要使用)。
该语言提供了为函数的参数提供默认值的支持,这样,如果未传递默认值,则编译器将插入默认值。
可能有人认为这应该是对可选参数建模的最佳方法。但是,他们有三个问题。
您不一定总是具有默认值,有时您只想知道该值是否已传递。例如,一个标志。
如果它在自己的参数组上,则仍然需要添加看起来很丑陋的空括号(这当然是主观意见)。
def transact[A](config: Config = Config.default)(f: Transaction => A): A
transact()(tx => ???)
object Functions {
def run[A](query: Query[A], config: Config = Config.default): A = ???
def run[A](query: String, config: Config = Config.default): A = ???
}
错误:在对象函数中,方法运行的多个重载替代方法定义了默认参数。
另一个常见的解决方法是提供该方法的重载版本。例如:
def test(required: Int, optional: String): String =
optional * required
def test(required: Int): String =
test(required, optional = "")
这一优点是,它封装了样板的按定义站点而不是按请求站点;还使代码更易于阅读,并得到工具的良好支持。
然而,最大的缺点是,如果您有多个可选参数,则无法很好地扩展。例如,对于三个参数,您需要七个(7
)重载。
但是,如果您有许多可选参数,则最好只请求一个Config
/Context
参数并使用Builder可能会更好。
def foo(data: Dar, config: Config = Config.default)
// It probably would be better not to use a case class for binary compatibility.
// And rather define your own private copy method or something.
// But that is outside of the scope of this question / answer.
final case class Config(
flag1: Option[Int] = None,
flag2: Option[Int] = None,
flag3: Option[Int] = None
) {
def withFlag1(flag: Int): Config =
this.copy(flag1 = Some(flag))
def withFlag2(flag: Int): Config =
this.copy(flag2 = Some(flag))
def withFlag3(flag: Int): Config =
this.copy(flag3 = Some(flag))
}
object Config {
def default: Config = new Config()
}
关于贡献者的论述,已建议为此用例添加语言级别或stdlib级别的支持。但是,由于上述相同的原因,所有这些都已被丢弃。
此类建议的示例:
与往常一样,根据您的具体情况和要提供的API选择要使用的技术。
也许引入并集类型可以为编码可选参数提供更简单的方法?
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句