正如标题所说,在 中建模 optional 参数的最佳方法是什么?斯卡拉 ?
对于 optional 参数,我的意思是执行函数体不需要的值。
要么因为该参数存在默认值,要么根本不需要该参数本身(例如配置或调试标志);请注意,在 Java 我可能会通过 null
对这些论点。
这是 上的常见问题解答斯卡拉 社区,尤其是新人制作的。
例如:
最佳答案
简单且社区接受的答案
总的来说,社区一致认为下面列出的所有提案或替代方案都不值得进行权衡。
因此,推荐的解决方案是只使用 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 参数(当然,如果它的名称不同)并且这种转换可以应用于代码的许多其他(非预期)部分;这就是为什么隐式转换 ,一般来说,是不鼓励的。一个次要的选择是使用 扩展方法 而不是 隐式转换 ,类似
optional = "foo".opt
.然而,事实是扩展方法要添加更多代码,并且该站点调用仍然有一些样板文件,这使得它像一个平庸的中间点。(免责声明,如果您正在使用 猫 ,您已经拥有这样一个 扩展方法 在范围
.some
中,因此您可能想要使用它)。默认参数
该语言支持为函数的参数提供默认值,这样如果未通过,编译器将插入默认值。
有人可能认为这应该是建模 optional 参数的最佳方式;然而,他们遇到了三个问题。
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 = ???
}
error: in object Functions, multiple overloaded alternatives of method run define default arguments.
重载
另一种常见的解决方法是提供该方法的重载版本;例如:
def test(required: Int, optional: String): String =
optional * required
def test(required: Int): String =
test(required, optional = "")
这个的优点是它封装了样板定义站点而不是调用站点;还使代码更易于阅读,并且得到了工具的良好支持。然而,最大的缺点是,如果您有多个 optional 参数,这将无法很好地扩展;例如,对于三个参数,您需要七个 (
7
) 重载。但是,如果您有许多 optional 参数,也许最好只要求一个
Config
/Context
参数并使用 build 师 .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()
}
请求本地支持在贡献者的话语中,有人提议为此用例添加语言级别或标准库级别的支持。但是,由于上述相同的原因,它们都已被丢弃。
此类提案的示例:
结论
与往常一样,根据您的具体情况和您想要提供的 API 选择要使用的技术。
斯卡拉 3
也许是的介绍联合类型 可以打开一种更简单的方法来编码 optional 参数的可能性吗?
关于scala - 建模 optional 参数的最佳方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65256690/