在Stackoverflow上已经有很多关于在Java中使用 Optional 的正确方法的讨论(像this one,or this这样的讨论)
截至目前,在Java中使用 Optional 作为类成员被广泛认为是一种代码气味,甚至因故意不实现 Serializable 接口而气馁。此外,我们应该在DTO,构造函数和方法的输入参数中避免它。从OOP的角度来看,到目前为止我所读到的关于 Optional 的所有内容都吸引了我的理由。
我的问题是,Scala的FP端是否会以某种方式改变某些内容 Optional ?特别是因为在Scala中实现 Optional 似乎更加丰富。我发现很多文章描述如何在Scala中使用它,但没有一篇文章耗尽主题当应该使用它时 我不应该。
答案 0 :(得分:9)
Option
个字段有用例;他们本质上并不坏。然而,即使几个成熟的库(例如this answer)定义了具有Option
字段的类,后者,IMO,往往是代码气味,因为它们经常试图为自己的利益做太多
在许多情况下,包含可选字段的类型可以轻松有利地用代数数据类型替换。
考虑处理帐户的业务域。帐户有一天会以开放帐户开始,但最终可能会关闭。除其他数据外,帐户包含开放和关闭的日期(如适用)。
Option
字段以下是帐户的实施,使用Option
字段:
final case class Account(openedOn: LocalDate, closedOn: Option[LocalDate], ...)
我们还有一个帐户服务,除其他外,还定义了close
方法:
trait AccountService {
// ...
def close(account: Account): Account
}
出于多种原因,这种方法存在问题。一个问题是Account
并不是特别有效:因为closedOn
是一个"盒装"类型,你有一个层次的间接太多,可以这么说。此外,Account
的内存占用量还不尽如人意:a"已关闭的帐户"包含一个非常无趣的值(None
),这是浪费空间。
另一个更严重的问题是,close
方法无法在类型级别强制执行参数为"开放帐户"结果是一个"已关闭的账户"。您必须编写测试以检查您的实现是否强制执行此业务规则。
Option
字段)考虑以下替代设计:
sealed trait Account { ... }
final case class OpenAccount(openedOn: LocalDate, ...) extends Account
final case class ClosedAccount(openedOn: LocalDate, closedOn: LocalDate, ...) extends Account
这个小型ADT可以解决性能问题,但还有更多......您现在可以在类型级别对业务规则进行编码!这是使非法国家无法代表的一个例子(Yaron Minsky的一句话)。因此,您服务的API变得更具表现力,ScalaTest:
trait AccountService {
// ...
def close(account: OpenAccount): ClosedAccount
}
这个例子可能足以让你相信第二种方法更可取,并且最好避免使用Option
字段(或至少谨慎使用)。
有关消除使非法国家无法代表的可选字段的更多信息,请参阅
答案 1 :(得分:8)
Option scala实现Serializable
强烈建议在可变属性中使用scala中的Option
。 Option[T]
被认为优于T
,因为前者比后者更安全。
截至目前,在Java中使用Optional作为类成员的做法很普遍 被认为是代码气味
相反,在scala中使用null
替换可选属性被视为代码气味。
尽管Scala是一种功能语言,但它也是一种促进类型安全的语言。在理想世界中,真正完全类型安全的语言不会有NullpointerException
等运行时异常,而Option
在Scala中扮演重要角色以避免它。
Option [T]表明属性可以处于null状态(即None
)并强制属性的客户端处理null
场景。因此,Option会向类型系统添加更多信息,并使代码更加类型安全。
使用模式匹配和Monad / Monoid等语言功能,在Scala中使用可选数据类型的经济性在Scala中非常便宜且用户友好,与Java相比。
模式匹配:
optionalVariable match {
case Some(x) => /* handle when variable has a value*/
case None => /* handle when the variable doesn't have a value*/
}
选项为Monad :
optionalVariable foreach { x => /* run code when the variable is defined*/ }
optionalVariable map { x => /* map optional type to another type */}
修改强>:
Jubobs非常适合使用Option替换为自定义类型的情况。但我认为还有更多的情况,其中可选属性更有意义。例如:如果Account对象具有可选属性,例如emailId
和phoneNo
,则Option [T]将是更好的解决方案,因为为每个组合创建自定义类型将是不切实际的并且会导致类爆炸。