我有一个案例,我希望根据(少数,比如5到10)选项的存在对对象应用修改。所以基本上,如果我要强制执行,我的目标是:
var myObject = ...
if (option.isDefined) {
myObject = myObject.modify(option.get)
}
if (option2.isDefined) {
myObject = myObject.someOtherModification(option2.get)
}
(请注意:也许我的目标是可变的,也许不是,这不是重点。)
我认为如果我尝试用这种方式编写流利的方式会更好看,例如(伪代码......):
myObject.optionally(option, _.modify(_))
.optionally(option2, _.someOtherModification(_))
所以我开始使用示例代码,其中intelliJ没有突出显示为错误,但实际上并没有构建。
class MyObject(content: String) {
/** Apply a transformation if the optional is present */
def optionally[A](optional: Option[A], operation: (A, MyObject) => MyObject): MyObject =
optional.map(operation(_, this)).getOrElse(this)
/** Some possible transformation */
def resized(length : Int): MyObject = new MyObject(content.substring(0, length))
}
object Test {
val my = new MyObject("test")
val option = Option(2)
my.optionally(option, (size, value) => value.resized(size))
}
现在,在我的情况下,MyObject
类型是一些外部API,所以我创建了一个隐式转换来帮助,所以它实际上是这样的:
// Out of my control
class MyObject(content: String) {
def resized(length : Int): MyObject = new MyObject(content.substring(0, length))
}
// What I did : create a rich type over MyObject
class MyRichObject(myObject: MyObject) {
def optionally[A](optional: Option[A], operation: (A, MyObject) => MyObject): MyObject = optional.map(operation(_, myObject)).getOrElse(myObject)
}
// And an implicit conversion
object MyRichObject {
implicit def apply(myObject: MyObject): MyRichObject = new MyRichObject(myObject)
}
然后,我这样使用它:
object Test {
val my = new MyObject("test")
val option = Option(2)
import MyRichObject._
my.optionally(option, (size, value) => value.resized(size))
}
这一次,它在IntelliJ和编译时失败,因为Option
的类型未知:
Error:(8, 26) missing parameter type
my.optionally(option, (size, value) => value.resized(size))
为了使它成功,我可以:
size
参数的类型:my.optionally(option, (size: Int, value) => value.resized(size))
optionally
重写为curried-version 它们都不是真的很糟糕,但如果我可以问:
foldLeft
...)?答案 0 :(得分:2)
供您考虑的一个选项:
// Out of my control
class MyObject(content: String) {
def resized(length : Int): MyObject = new MyObject(content.substring(0, length))
}
object MyObjectImplicits {
implicit class OptionalUpdate[A](val optional: Option[A]) extends AnyVal {
def update(operation: (A, MyObject) => MyObject): MyObject => MyObject =
(obj: MyObject) => optional.map(a => operation(a, obj)).getOrElse(obj)
}
}
object Test {
val my = new MyObject("test")
val option = Option(2)
import MyObjectImplicits._
Seq(
option.update((size, value) => value.resized(size)),
// more options...
).foldLeft(my)(_)
}
也可以像你说的那样使用optionally
的咖喱版。
答案 1 :(得分:1)
考虑需要添加类型的更好方法是这样写:
object Test {
val my = new MyObject("test")
val option = Some(2)
my.optionally[Int](option, (size, value) => value.resized(size))
}
另一种方式,如果你自创建对象后只管理一种类型,就是将泛型移动到类创建,但要小心,使用此选项,每个实例只能有一种类型:
class MyObject[A](content: String) {
def optionally(optional: Option[A], operation: (A, MyObject[A]) => MyObject[A]): MyObject[A] =
optional.map(operation(_, this)).getOrElse(this)
def resized(length : Int): MyObject[A] = new MyObject[A](content.substring(0, length))
}
object Test {
val my = new MyObject[Int]("test")
val option = Some(2)
my.optionally(option, (size, value) => value.resized(size))
}
正如你所看到的,现在所有generics所在的地方都是由Int类型占用的,因为这首先是你想要的,这里有一个很好的答案说明原因:
(我认为这里适用的部分是:)
4)当推断的返回类型比你想要的更通用时,例如Any。