mis*_*tor 82 java monads null functional-programming scala
我无法理解Option[T]Scala中的课程要点.我的意思是,我不能看到任何advanages None了null.
例如,考虑代码:
object Main{
class Person(name: String, var age: int){
def display = println(name+" "+age)
}
def getPerson1: Person = {
// returns a Person instance or null
}
def getPerson2: Option[Person] = {
// returns either Some[Person] or None
}
def main(argv: Array[String]): Unit = {
val p = getPerson1
if (p!=null) p.display
getPerson2 match{
case Some(person) => person.display
case None => /* Do nothing */
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在假设,该方法getPerson1返回null,然后display对第一行的调用main绑定失败NPE.同样,如果getPerson2返回None,则display调用将再次失败并出现类似的错误.
如果是这样,那么为什么Scala通过引入一个新的值包装器(Option[T])而不是遵循Java中使用的简单方法来使事情复杂化?
更新:
我根据@Mitch的建议编辑了我的代码.我仍然无法看到任何特别的优势Option[T].我必须测试特殊情况null或None两种情况.:(
如果我从@ Michael的回复中正确理解,唯一的好处Option[T]是它明确地告诉程序员这个方法可以返回None吗?这是设计选择背后的唯一原因吗?
Dan*_*ral 72
Option如果你强迫自己永远不会使用,你会得到更好的结果get.那是因为get相当于"好吧,把我送回零土地".
所以,举个你的例子吧.你怎么display不用不用电话get?以下是一些替代方案:
getPerson2 foreach (_.display)
for (person <- getPerson2) person.display
getPerson2 match {
case Some(person) => person.display
case _ =>
}
getPerson2.getOrElse(Person("Unknown", 0)).display
Run Code Online (Sandbox Code Playgroud)
这些替代方案都不会让你调用display不存在的东西.
至于为什么get存在,Scala不告诉你应该如何编写代码.它可能会轻轻地刺激你,但如果你想回到没有安全网,这是你的选择.
你在这里钉了它:
Option [T]的唯一优点是它明确地告诉程序员这个方法可以返回None吗?
除了"唯一".但让我重申,以另一种方式:在主要的优势Option[T]在T是类型安全.它确保您不会将T方法发送到可能不存在的对象,因为编译器不会让您.
你说你必须在两种情况下测试可空性,但如果你忘记 - 或者不知道 - 你必须检查null,编译器会告诉你吗?或者你的用户?
当然,由于它与Java的互操作性,Scala像Java一样允许空值.因此,如果您使用Java库,如果您使用写得不好的Scala库,或者如果您使用写得不好的个人 Scala库,那么您仍然需要处理空指针.
Option我能想到的另外两个重要优点是:
文档:方法类型签名将告诉您是否始终返回对象.
Monadic可组合性.
后者需要更长时间才能完全理解,并且它不适合简单的示例,因为它只显示其在复杂代码上的强度.所以,我将在下面给出一个例子,但我很清楚,除了那些已经获得它的人之外,它几乎没有任何意义.
for {
person <- getUsers
email <- person.getEmail // Assuming getEmail returns Option[String]
} yield (person, email)
Run Code Online (Sandbox Code Playgroud)
Syn*_*sso 31
相比:
val p = getPerson1 // a potentially null Person
val favouriteColour = if (p == null) p.favouriteColour else null
Run Code Online (Sandbox Code Playgroud)
有:
val p = getPerson2 // an Option[Person]
val favouriteColour = p.map(_.favouriteColour)
Run Code Online (Sandbox Code Playgroud)
monadic属性bind作为map函数出现在Scala中,允许我们对对象进行链接操作,而不必担心它们是否为"null".
再举一个这个简单的例子吧.假设我们想找到所有人喜爱的颜色.
// list of (potentially null) Persons
for (person <- listOfPeople) yield if (person == null) null else person.favouriteColour
// list of Options[Person]
listOfPeople.map(_.map(_.favouriteColour))
listOfPeople.flatMap(_.map(_.favouriteColour)) // discards all None's
Run Code Online (Sandbox Code Playgroud)
或许我们想找到一个人的父亲的母亲的妹妹的名字:
// with potential nulls
val father = if (person == null) null else person.father
val mother = if (father == null) null else father.mother
val sister = if (mother == null) null else mother.sister
// with options
val fathersMothersSister = getPerson2.flatMap(_.father).flatMap(_.mother).flatMap(_.sister)
Run Code Online (Sandbox Code Playgroud)
我希望这能说明选择如何让生活变得更轻松.
Mic*_*ale 22
差异很微妙.请记住,要真正成为一个函数,它必须返回一个值 - 在这个意义上,null实际上并不被认为是"正常的返回值",更多的是底层类型 /没有.
但是,在实际意义上,当你调用一个可选择返回某个东西的函数时,你会这样做:
getPerson2 match {
case Some(person) => //handle a person
case None => //handle nothing
}
Run Code Online (Sandbox Code Playgroud)
当然,你可以用null做类似的东西 - 但是这使得getPerson2通过它返回的事实使调用的语义变得明显Option[Person](一个很好的实用的东西,除了依赖于阅读文档并获得NPE的人,因为他们没有阅读DOC).
我会尝试挖掘一个功能强大的程序员,他能给出比我更严格的答案.
par*_*tic 15
对我来说,在理解语法处理时,选项非常有趣.以synesso为例:
// with potential nulls
val father = if (person == null) null else person.father
val mother = if (father == null) null else father.mother
val sister = if (mother == null) null else mother.sister
// with options
val fathersMothersSister = for {
father <- person.father
mother <- father.mother
sister <- mother.sister
} yield sister
Run Code Online (Sandbox Code Playgroud)
如果任何分配是None,fathersMothersSister将会是,None但不会NullPointerException被提出.然后,您可以安全地传递fathersMothersSister给一个采用Option参数的函数,而不必担心.所以你不检查null,你不关心异常.将其与synesso示例中提供的java版本进行比较.
您可以使用Option具有非常强大的组合功能:
def getURL : Option[URL]
def getDefaultURL : Option[URL]
val (host,port) = (getURL orElse getDefaultURL).map( url => (url.getHost,url.getPort) ).getOrElse( throw new IllegalStateException("No URL defined") )
Run Code Online (Sandbox Code Playgroud)
小智 8
也许其他人指出了这一点,但我没有看到它:
使用Option [T]与null检查进行模式匹配的一个优点是Option是一个密封类,因此如果您忽略编写Some或None情况的代码,Scala编译器将发出警告.编译器有一个编译器标志,可以将警告转换为错误.因此,可以防止在编译时而不是在运行时处理"不存在"的情况.与使用空值相比,这是一个巨大的优势.
它没有帮助避免空检查,它是强制空检查.当你的类有10个字段时,这一点就变得清晰了,其中两个字段可能为null.您的系统还有50个其他类似的类.在Java世界中,您尝试使用mental horesepower,命名约定或甚至注释的某些组合来阻止这些字段上的NPE.每个Java开发人员都在这方面失败了很多.Option类不仅使任何试图理解代码的开发人员在视觉上清楚地看到"可空"值,而且允许编译器强制执行此先前未说明的合同.
[临摹此评论由丹尼尔Spiewak ]
如果使用的唯一方法
Option是模式匹配以获得值,那么是的,我同意它并没有改善null.但是,你错过了一个*巨大的*类功能.唯一令人信服的理由Option是,如果您正在使用其高阶效用函数.实际上,你需要使用它的monadic性质.例如(假设一定量的API修剪):Run Code Online (Sandbox Code Playgroud)val row: Option[Row] = database fetchRowById 42 val key: Option[String] = row flatMap { _ get “port_key” } val value: Option[MyType] = key flatMap (myMap get) val result: MyType = value getOrElse defaultValue在那里,不是那么漂亮吗?如果我们使用
for-comprehensions,我们实际上可以 做得更好:Run Code Online (Sandbox Code Playgroud)val value = for { row <- database fetchRowById 42 key <- row get "port_key" value <- myMap get key } yield value val result = value getOrElse defaultValue您会注意到我们*从不*明确检查null,None或其任何类似的东西.Option的重点是避免任何检查.你只需将计算字符串串联起来并向下移动,直到你真的*需要得到一个值.在这一点上,你可以决定你是否愿意做明确的检查(你应该永远要做的),提供默认值,抛出异常等.
我从来没有做过任何明确的匹配
Option,而且我知道很多其他Scala开发人员都在同一条船上.前几天David Pollak向我提到他使用这种显式匹配Option(或者Box,在Lift的情况下)作为编写代码的开发人员不完全理解语言及其标准库的标志.我并不是说要成为一个巨魔锤,但你真的需要看看语言特征在实践中是如何实际*在你将它们打成无用之前使用的.我绝对同意Option是非常不引人注目的,因为*你*使用它,但你没有按照设计的方式使用它.
这里没有其他人似乎提出的一点是,虽然你可以有一个空引用,但是Option引入了一个区别.
也就是说,你可以有Option[Option[A]],这将由有人居住None,Some(None)并Some(Some(a))在那里a是通常的居民之一A.这意味着如果你有某种容器,并希望能够在其中存储空指针并将它们取出,你需要传回一些额外的布尔值来知道你是否真的得到了一个值.这样的疣在java容器API中比比皆是,而一些无锁的变体甚至无法提供它们.
null 是一种一次性的结构,它不与自身构成,它只适用于参考类型,它迫使你以非完全的方式推理.
例如,当你检查
if (x == null) ...
else x.foo()
Run Code Online (Sandbox Code Playgroud)
你必须在整个else分支中随身携带x != null并且已经检查过了.但是,在使用类似选项的东西时
x match {
case None => ...
case Some(y) => y.foo
}
Run Code Online (Sandbox Code Playgroud)
你知道 y不是None建筑 - 而且你知道它也不null是,如果不是因为Hoare 十亿美元的错误.
| 归档时间: |
|
| 查看次数: |
9123 次 |
| 最近记录: |