我有一些具有相同编码模式的函数:
def updateFooValidationStatus(fooId: Long, user: String, valid: Boolean): Option[Foo] = {
val fooMaybe = fooDao.getFooById(activityId)
fooMaybe match {
case Some(foo) => {
fooDao.update(foo.copy(updatedBy = Some(user),
validationStatus = if (valid) Some(DataEntryValidationStatus.Valid) else Some(DataEntryValidationStatus.Invalid))
)
}
case None =>
throw new DomainException(s"Foo with ID: '$fooId' doesn't exist")
}
}
为了减少重复我的代码,我编写了一个新的私有函数
private def updateDomainObjectValidationStatus(f1: Long => Option[DomainObject],
parameter1: Long,
f2: DomainObject => Option[DomainObject],
paramter2: String,
valid: Boolean ): Option[DomainObject] ={
val somethingMaybe = f1(parameter1)
somethingMaybe match {
case Some(something) =>
f2(
something.copyMe(updatedBy = Some(paramter2),
validationStatus = if(valid) Some(DataEntryValidationStatus.Valid) else Some(DataEntryValidationStatus.Invalid))
)
case None =>
throw new DomainException(s"Object with ID: '$parameter1' doesn't exist")
}
}
,其中
trait DomainObject { ... }
case class Foo( ... ) extends DomainObject { ... }
根据上述更改,由于一个参数上的错误,我无法在updateFooValidationStatus内调用updateDomainObjectValidationStatus
type mismatch, expected (DomainObject) => Option[DomainObject], actual (Foo) => Option[Foo]
有趣的是,它并没有抱怨第一个参数
(Long) => Option[DomainObject]
需要
(Long) => Option[Foo]
Scala惯用方式中的代码设计将使上述代码有效吗?
答案 0 :(得分:4)
这里发生的事情是:updateDomainObjectValidationStatus
无法接受类型为(Foo) => Option[Foo]
的值作为(DomainObject) => Option[DomainObject]
类型的参数,因为函数是逆变< / em>在他们的参数类型和协变的返回类型。这是什么意思?
A
扩展B
,则函数返回 A
会扩展一个返回B
的函数(这就是为什么编译器没有&## 39;抱怨你的第一个参数)A
扩展B
,那么类型为B
的参数的函数会扩展名为A
的参数的函数 - 意思是,反过来!为什么这有意义?
考虑一下:如果A extends B
,我们可以说&#34; A是B&#34;。现在,&#34; A&#34;可以使用任何A
,但不一定适用于任何B
,对吧?如果你的函数有一个Int
参数,你就不能传递一个AnyVal
类型的值...反之亦然 - AnyVal
上的一个函数绝对可以调用Int
。
要解决此问题 - 在这种情况下,看起来最好的方法是为updateDomainObjectValidationStatus
提供扩展DomainObject
的类型:
private def updateDomainObjectValidationStatus[T <: DomainObject](
f1: Long => Option[T],
parameter1: Long,
f2: T => Option[T],
paramter2: String,
valid: Boolean): Option[T] = { ... }
当您使用类型为(Long) => Option[Foo]
的第一个参数调用它时,类型T
被推断为Foo
,然后编译器期望(Foo) => Option[Foo]
为第三个参数,如预期。你甚至可以得到一个很好的奖励 - 返回类型会更具体:Option[Foo]
而不是Option[DomainObject]
。