假设我的域名对象名为" Office" :
case class Office(
id: Long,
name: String,
phone: String,
address: String
) {
def this(name: String, phone: String, address: String) = this(
null.asInstanceOf[Long], name, phone, address
)
}
当我创建新的 Office 时:
new Office("officeName","00000000000", "officeAddress")
我没有指定 id 字段,因为我不知道。当我保存办公室时(通过Anorm)我现在 id 并执行此操作:
office.id = officeId
因此。我知道使用 null 是非Scala方式。如何避免在我的情况下使用 null ?
更新#1
使用选项。
假设,像这样:
case class Office(
id: Option[Long],
name: String,
phone: String,
address: String
) {
def this(name: String, phone: String, address: String) = this(
None, name, phone, address
)
}
并且,保存后:
office.id = Option(officeId)
但是如果我需要通过办公室ID找到什么呢?
SomeService.findSomethingByOfficeId(office.id.get)
清楚吗? office.id.get 看起来不那么好)
更新#2
每个人都要感谢!我从你的答案中得到了新的想法!致谢谢谢!
答案 0 :(得分:4)
为什么不将id字段声明为Option
?你应该避免在Scala中使用null。 Option
是首选,因为它是类型安全的,并且在功能范例中与其他结构一起使用时很好。
像(我没有测试过这段代码):
case class Office(
id: Option[Long],
name: String,
phone: String,
address: String
) {
def this(name: String, phone: String, address: String) = this(
None, name, phone, address
)
}
答案 1 :(得分:2)
只需将id
字段设为Option[Long]
;一旦你拥有了它,就可以像这样使用它:
office.id.map(SomeService.findSomethingByOfficeId)
这将执行您想要的操作并返回Option[Something]
。如果office.id
为None
,则map()
甚至不会调用finder方法,并会立即返回None
,这通常是您想要的。
如果findSomethingByOfficeId
返回Option[Something]
(它应该),而不仅仅是Something
或null
/例外,请使用:
office.id.flatMap(SomeService.findSomethingByOfficeId)
这样,如果office.id
为None
,它将再次立即返回None
;但是,如果它是Some(123)
,它会将123
传递给findSomethingByOfficeId
;现在,如果查找程序返回Some(something)
,它将返回Some(something)
,如果查找程序返回None
,它将再次返回None
。
如果findSomethingByOfficeId
可以返回null
并且您无法更改其源代码,请使用Option(...)
对其进行任何调用 - 这会将null
转换为None
并将Some(...)
中的任何其他值换行;如果它在找不到某个内容时可以抛出异常,请用Try(...).toOption
包装对它的调用以获得相同的效果(尽管这也会将任何不相关的异常转换为None
,这是可能不受欢迎,但您可以使用recoverWith
修复此问题。
一般准则总是避免null
和Scala代码中的例外(如您所述);始终更喜欢Option[T]
与map
或flatMap
链接,或使用monadic for
句法糖隐藏map
和flatMap
的使用。< / p>
可运行的示例:
object OptionDemo extends App {
case class Something(name: String)
case class Office(id: Option[Long])
def findSomethingByOfficeId(officeId: Long) = {
if (officeId == 123) Some(Something("London")) else None
}
val office1 = Office(id = None)
val office2 = Office(id = Some(456))
val office3 = Office(id = Some(123))
println(office1.id.flatMap(findSomethingByOfficeId))
println(office2.id.flatMap(findSomethingByOfficeId))
println(office3.id.flatMap(findSomethingByOfficeId))
}
<强>输出:强>
None
None
Some(Something(London))
有关Scala非常有用的Option[T]
类型的精彩介绍,请参阅http://danielwestheide.com/blog/2012/12/19/the-neophytes-guide-to-scala-part-5-the-option-type.html。
答案 2 :(得分:0)
使用id: Option[Long]
时,使用
if (office.id.isDefined) {
val Some(id) = office.id
SomeService.findSomethingByOfficeId(id)
}
或者可能是例如
office.id match {
case None => Array()
case Some(id) => SomeService.findSomethingByOfficeId(id)
}
您还可以按如下方式定义案例类和对象,
trait OId
case object NoId extends OId
case class Id(value: Long) extends OId
case class Office (
id: OId = NoId,
name: String,
phone: String,
address: String
)
请注意,默认id
为NoId
,无需声明对this
的调用。然后
val office = Office (Id(123), "name","phone","addr")
val officeNoId = Office (name = "name",phone="phone",address="addr")
如果最后定义了id
成员,则无需为成员名称命名,
val office = Office ("name","phone","addr")
office: Office = Office(name,phone,addr,NoId)
在调用(整齐地)方法时,
office.id match {
case NoId => Array()
case Id(value) => SomeService.findSomethingByOfficeId(value)
}
答案 3 :(得分:-1)
我更喜欢对象 Id 属性的强烈限制:
trait Id[+T] {
class ObjectHasNoIdExcepthion extends Throwable
def id : T = throw new ObjectHasNoIdExcepthion
}
case class Office(
name: String,
phone: String,
address: String
) extends Id[Long]
object Office {
def apply(_id : Long, name: String, phone: String, address: String) =
new Office(name, phone, address) {
override def id : Long = _id
}
}
如果我尝试为对象获取未存储在DB中的Id,我会得到异常,这意味着程序行为有问题。
val officeWithoutId =
Office("officeName","00000000000", "officeAddress")
officeWithoutId.id // Throw exception
// for-comprehension and Try monad can be used
// update office:
for {
id <- Try { officeWithoutId.id }
newOffice = officeWithoutId.copy(name = "newOffice")
} yield OfficeDAL.update(id, newOffice)
// find office by id:
for {
id <- Try { officeWithoutId.id }
} yield OfficeDAL.findById(id)
val officeWithId =
Office(1000L, "officeName","00000000000", "officeAddress")
officeWithId.id // Ok
<强>优点:强>
1)使用 id 参数的方法可以在DAL逻辑中封装
private[dal] def apply (_id : Long, name: String, ...
2)复制方法始终创建空 id 的新对象(如果更改数据则为安全)
3)更新方法是安全的(默认情况下不会覆盖对象, id 总是需要指定)
<强>缺点:强>
1)商店 id 属性需要特殊的serealization / deserealization逻辑(json用于webservices等)
<强> P.S。强> 如果您有不可变对象(ADT)并将其存储到具有id +对象版本而非对象替换的DB中,则此方法很好。