Scala 为可选参数建模的最佳方法
正如标题所说,在Scala中建模可选参数的最佳方法是什么 对于可选参数,我指的是执行函数体不需要的值Scala 为可选参数建模的最佳方法,scala,design-patterns,optional,Scala,Design Patterns,Optional,正如标题所说,在Scala中建模可选参数的最佳方法是什么 对于可选参数,我指的是执行函数体不需要的值 要么因为该参数存在默认值,要么根本不需要该参数本身(例如,配置或调试标志);请注意,在Java上,我可能会将null传递给这些参数 这是Scala社区的常见问题解答,特别是由新手编写的 例如: 在这里的SO: 关于用户的话语: 在GitterScala频道中: 简单且社区接受的答案 一般而言,社区一致认为,以下列出的所有建议或备选方案都不值得权衡。 因此,建议的解决方案是只使用数据类型并手动
要么因为该参数存在默认值,要么根本不需要该参数本身(例如,配置或调试标志);请注意,在Java上,我可能会将
null
传递给这些参数
这是Scala社区的常见问题解答,特别是由新手编写的 例如:
- 在这里的SO:
- 关于用户的话语:
- 在GitterScala频道中:
因此,建议的解决方案是只使用数据类型并手动/显式地将值包装在
Some
def测试(必需:Int,可选:选项[String]=None):String=
可选.map(*required).getOrElse(“”)
测试(要求=100)/“”
测试(必需=3,可选=Some(“Foo”)/“Foo”
然而,这种方法的明显缺点是在调用站点上有必要的样板文件。
但是,可以说,它使代码更易于阅读和理解,从而便于维护
尽管如此,有时您可以使用其他技术(如默认参数或重载)提供更好的API
备选方案和建议
隐式转换
由于前面解决方案的样板文件,使用隐式转换的常见替代方法被反复提及;例如:
implicit def a2opt[A](A:A):选项[A]=Some(A)
这样就可以像这样调用前面的函数:
测试(必需=3,可选=“Foo”)
这样做的缺点是,隐式转换隐藏了这样一个事实:可选
是一个可选参数(当然,如果名称不同的话),并且这种转换可以应用于代码的许多其他(非预期)部分;这就是为什么通常不鼓励隐式转换的原因
另一种方法是使用扩展方法而不是隐式转换,例如optional=“foo”.opt
。然而,扩展方法需要添加更多的代码,而且站点调用仍然有一些样板文件,这一事实使它看起来像一个平庸的中间点。(免责声明,如果您使用的是cats,那么您已经在范围
中使用了这样一种扩展方法
默认参数
该语言支持为函数的参数提供默认值,如果未传递,编译器将插入默认值
有人可能认为这应该是对可选参数建模的最佳方式;然而,他们有三个问题
您并不总是有默认值,有时您只想知道是否传递了该值。例如,国旗
如果它在自己的参数组中,您仍然需要添加空括号,这可能看起来很难看(当然,这是一种主观意见)
def transact[A](config:config=config.default)(f:Transaction=>A):A
transact()(tx=>??)
您只能有一个带有默认参数的重载
对象函数{
def run[A](查询:查询[A],配置:config=config.default):A=???
def run[A](查询:字符串,配置:config=config.default):A=???
}
错误:在对象函数中,方法run的多个重载替代项定义了默认参数
超载
另一个常见的解决方法是提供该方法的重载版本;例如:
def测试(必需:Int,可选:String):String=
可选*必填项
def测试(必需:Int):字符串=
测试(必需,可选=“”)
这种方法的优点是它将样板文件封装在定义站点上,而不是在调用站点上;还使代码更易于阅读,并得到工具的良好支持。
然而,最大的缺点是,如果您有多个可选参数,那么这不能很好地扩展;例如,对于三个参数,您需要七个(7
)重载
但是,如果您有许多可选参数,那么最好只要求一个Config
/Context
参数并使用Builder
构建器模式。
def foo(数据:Dar,配置:config=config.default)
//最好不要使用case类来实现二进制兼容性。
//而是定义自己的私有复制方法或其他东西。
//但这超出了本问题/答案的范围。
最终案例类配置(
flag1:选项[Int]=无,
flag2:选项[Int]=无,
flag3:选项[Int]=无
) {
def withFlag1(标志:Int):配置=
复制(flag1=Some(flag))
def withFlag2(标志:Int):配置=
这个。复制(flag2=Some(flag))
def withFlag3(标志:Int):配置=
复制(flag3=Some(flag))
}
对象配置{
def默认值:Config=new Config()
}
请求本地支持
在贡献者的论述中,有人建议为这个用例添加语言级或stdlib级支持。然而,由于上述相同的原因,所有这些都被丢弃了
这些建议的例子如下:
斯卡拉3 可能是引入了联合类型<