我当然对Scala很新,而且我在许多Scala示例中看到的语法糖有问题。 它通常会产生一个非常简洁的陈述,但老实说到目前为止(对我而言)有点难以理解。
因此,我希望将Option类(安全解除引用)的典型用法作为开始理解的好地方,例如,在我已经看到的特定示例中使用下划线。
我发现了一篇非常好的文章,展示了使用Option来避免null的情况。
https://medium.com/@sinisalouc/demystifying-the-monad-in-scala-cc716bb6f534#.fhrljf7nl
他描述了一个用法:
trait User {
val child: Option[User]
}
顺便说一句,您也可以将这些函数编写为就地lambda 功能而不是先验地定义它们。然后代码变成了 这样:
val result = UserService.loadUser("mike")
.flatMap(user => user.child)
.flatMap(user => user.child)
看起来很棒!也许不像人们在groovy中那样简洁,但也不错。
所以我想我会尝试将它应用到我想要解决的案例中。
我有一个类型Person
,其中Person
的存在是可选的,但如果我们有一个人,他的属性就会得到保证。因此,Option
类型本身并未使用Person
类型。
Person
的PID类型为Id
。 Id
类型包含两种String
类型; Id-Type和Id-Value。
我已使用Scala控制台测试以下内容:
class Id(val idCode : String, val idVal : String)
class Person(val pid : Id, val name : String)
val anId: Id = new Id("Passport_number", "12345")
val person: Person = new Person(anId, "Sean")
val operson : Option[Person] = Some(person)
行。这设置了我的人和它的可选实例。
我从上面的链接文章中了解到,我可以使用flatMap获得Persons Id-Val;像这样:
val result = operson.flatMap(person => Some(person.pid)).flatMap(pid => Some(pid.idVal)).getOrElse("NoValue")
大!这样可行。如果我事实上没有人,我的结果是" NoValue"。
我使用flatMap(而不是Map)因为,除非我误解(并且我的Map测试不正确)如果我使用Map我必须提供备用或默认Person实例。我不想这么做。
好的,所以,flatMap是要走的路。
但是,这真的不是一个非常简洁的陈述。 如果我以更加时髦的风格写作,我想我能够做到这样的事情:
val result = person?.pid.idVal
哇,那好一点!
当然,Scala有能力提供至少与Groovy一样好的东西吗?
在上面的链接示例中,他能够使用我之前提到的一些语法糖使他的陈述更简洁。下划线:
或者更简洁:
val result = UserService.loadUser("mike")
.flatMap(_.child)
.flatMap(_.child)
因此,在这种情况下,下划线字符似乎允许您跳过指定类型(推断类型)并用下划线替换它。
然而,当我用我的例子尝试同样的事情时:
val result = operson.flatMap(Some(_.pid)).flatMap(Some(_.idVal)).getOrElse("NoValue")
斯卡拉抱怨道。
<console>:15: error: missing parameter type for expanded function ((x$2) => x$2.idVal)
val result = operson.flatMap(Some(_.pid)).flatMap(Some(_.idVal)).getOrElse("NoValue")
有人可以帮助我吗?
我怎么误解这个? 是否有一种简短的方法来编写我上面冗长的陈述? flatMap是实现我追求目标的最佳方式吗?或者是否有更简洁和/或可读的方法来做到这一点?
提前感谢!
答案 0 :(得分:9)
为什么坚持使用flatMap?我只是使用map
代替你的例子:
val result = operson.map(_.pid).map(_.idVal).getOrElse("NoValue")
甚至更短:
val result = operson.map(_.pid.idVal).getOrElse("NoValue")
您只应将flatMap
与返回Option
的函数一起使用。您的pid
和idVal
不是Option
,所以只需映射它们。
你说
我有一个Person类型,其中Person的存在是可选的,但如果我们有一个人,他的属性是有保证的。因此,在Person类型本身中没有使用Option类型。
这是您的示例与User
示例之间的本质区别。在User
示例中,User
实例及其child
字段的存在都是选项。这就是为什么要获得child
,您需要flatMap
。但是,在您的示例中,只有Person
的存在才能得到保证,在您检索到Option[Person]
之后,您可以安全地映射到其任何字段。
将flatMap
视为map
,然后是flatten
(因此得名)。如果我映射到孩子:
val ouser = Some(new User())
val child: Option[Option[User]] = ouser.map(_.child)
我最终得到Option[Option[User]]
。我需要将其展平为单Option
级,这就是我首先使用flatMap
的原因。
答案 1 :(得分:7)
如果您正在寻找最简洁的解决方案,请考虑以下事项:
02-08 11:12:29.126 9112-9112/com.example.emiliomorillanieto.practica3 E/AndroidRuntime﹕ FATAL EXCEPTION: main
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.emiliomorillanieto.practica3/com.example.emiliomorillanieto.practica3.Movimiento}: android.view.InflateException: Binary XML file line #39: Error inflating class <unknown>
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2305)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2363)
at android.app.ActivityThread.access$900(ActivityThread.java:161)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1265)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:157)
at android.app.ActivityThread.main(ActivityThread.java:5356)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1265)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1081)
at dalvik.system.NativeStart.main(Native Method)
Caused by: android.view.InflateException: Binary XML file line #39: Error inflating class <unknown>
…
Caused by: java.lang.reflect.InvocationTargetException
…
Caused by: java.lang.OutOfMemoryError
…
虽然人们可能会发现它不清楚或令人困惑