自动在Options中包装参数

时间:2015-04-09 13:46:41

标签: scala

在很多情况下,我使用Option形式的参数作为默认值None - 如下所示:

def foo(bar:Option[String] = None)

这很方便,让我可以轻松省略null。但是,如果我这样做,我需要将方法的每次调用更改为

foo(Some("bar"))

而不是简单

foo("bar")

然而,这看起来有点多余,因为很明显,当我指定一个值时,它是一个完整的选项。我很确定我可以尝试编写一些隐式转换器来为我做这样的包装 - 不幸的是,我不知道如何。

奖金 - 这是理智的做法吗?有没有其他方法来处理" nullable"参数?

5 个答案:

答案 0 :(得分:6)

我会给你一些选择(双关语)。


请勿使用可选参数,而是使用Option.apply。当参数不一定是可选的,但您希望处理可能传递的null值时,这非常有用。

 def foo(bar: String): ?? = Option(bar)... // operate on the Option[String]

这样做的好处是Option.apply会自动将null转换为None,因此绝对不需要使用if / else。


对非可选参数使用重载。当参数真正是可选的时,情况更是如此,但是为您提供了传递Option包装或解包的便利性。但是,如果不首先知道类型,就不可能在此处传递null

def foo(bar: String): ?? = foo(Option(bar))

def foo(bar: Option[String]): ?? = ???

示例:

def foo(bar: String): Option[String] = foo(Option(bar))

def foo(bar: Option[String]): Option[String] = bar.map(_ + "aaa")

scala> foo("bbb")
res7: Option[String] = Some(bbbaaa)

scala> foo(null: String) // The String ascription is necessary here.
res9: Option[String] = None

scala> val str: String = null
scala> foo(str)  // No ascription necessary, since we know the type.
res10: Option[String] = None

将任何内容隐式转换为Option

implicit def any2Opt[A](value: A): Option[A] = Option(value)

并保持目前的定义

def foo(bar: Option[String]): ?? = ???

隐式转换为Option会导致一些意想不到的结果,所以要小心。

答案 1 :(得分:1)

您可以将所需的通用隐式写为

implicit def wrapToOption[T](x: T) = Option[T](x)

然后你可以做

def foo(bar: Option[String] = None) = println(bar)
foo("bar") //> Some(bar)
foo()      //> None

def fooBar(bar: Option[Int] = None) = println(bar)
fooBar(2) //> Some(2)

关于这是理智的事情,我会说不(个人意见)。众所周知,Implicits很难调试。错误的逻辑错误可能会让你的生活陷入困境。

此外,团队的每一个新成员都必须讲授所有这些"魔法"发生在幕后。

答案 2 :(得分:1)

将参数类型设置为Option[_]是一种非常合理的方法,如果它们确实是可选的。但是,我不建议使用implicits直接转换为Option[_]

通过包含Scalaz并使用some,您可以使语法更容易:

foo("hello".some)

如果您不想为此引入Scalaz,那么编写自己的隐式代码非常容易。这种隐式更好,因为你明确地调用some方法来“触发”隐式,而不是“魔法”转换。

另一种选择,如果你经常调用一个参数设置为Some[_]的函数来重载它(同样,我也不是过载的粉丝):

def foo(x: Option[String] = None, y: Option[Int] = None, z: Option[String] = None) { /* do stuff */ }

def foo(x: String, y: Int, z: Option[String] = None) = foo(x.some, y.some, z)

最后说明,如果函数明确将它们定义为可选,我认为在Some中包装参数没有任何问题。我不会太担心这种语法。

答案 3 :(得分:0)

您可以将隐式转换器定义为:

   implicit def conv(str: String): Option[String] = {
      if (str == null) None else Some(str)
    }

    def foo(bar: Option[String] = None) = {
      bar
    }

输出:

scala> foo("xyz")
res55: Option[String] = Some(xyz)
scala> foo()
res1: Option[String] = None

答案 4 :(得分:0)

将所有内容隐式转换为Option是一种危险的游戏,我会避免!您可能更喜欢简单地使用一些方法重载:

object Foos {
    private def foo(bar: Option[String]): Unit = println(bar.getOrElse("foobar"))
    def foo(bar: String): Unit = foo(Some(bar))
    def foo(): Unit = foo(None)
}

然后你可以这样做:

Foos.foo("barfoo") //prints "barfoo"
Foos.foo() //prints "foobar"

虽然仍然只是真正实施一次方法。此外,您可以通过这种方式隐藏Option重载。