Option
类有一个名为fold()
的方法。文档说:
sealed abstract class Option[+A]
fold[B](ifEmpty: ⇒ B)(f: (A) ⇒ B): B
如果scala.Option为非空,则返回将 f 应用于此scala.Option的值的结果。否则,评估表达式 ifEmpty 。
文档继续:
这相当于 scala.Option map f getOrElse ifEmpty 。
但这是真的吗?我被告知,在某些情况下,某些类型的价值观存在差异,但从来没有得到合理的解释。这两种结构的表现方式究竟是什么?为什么会这样?
答案 0 :(得分:4)
Option.fold
比.getOrElse
更安全。您可以在下面.fold
看到ifEmpty
和f
类型B
的定义。(仅在scala 2.10之后引入)
@inline final def fold[B](ifEmpty: => B)(f: A => B): B =
if (isEmpty) ifEmpty else f(this.get)
这意味着你可能不会弄乱数据类型(下面的例外),
scala> val data = Option("massive data").fold(-1) { _ => 1 }
data: Int = 1
// but if i try to return different type in either of ifEmpty or f
// compiler will curse me right at my face
scala> val data = Option("massive data").fold(-1) { _ => "Let me caught by compiler" }
<console>:17: error: type mismatch;
found : String("Let me caught by compiler")
required: Int
val data = Option("massive data").fold(-1) { _ => "Let me caught by compiler" }
^
除非您手动提供类型,否则getOrElse
不会更安全。
@inline final def getOrElse[B >: A](default: => B): B =
if (isEmpty) default else this.get
表示您可以从getOrElse
返回与Option[T]
中包含的原始值不同的其他类型。
scala> val data = Option("massive data").map(_ => 1).getOrElse(List("I'm not integer"))
data: Any = 1
//you have to manually mention the type to getOrElse to restrict,
// which is not that smart in my opinion
scala> val data = Option("massive data").map(_ => 1).getOrElse[Int](List("I'm not integer"))
<console>:17: error: type mismatch;
found : List[String]
required: Int
val data = Option("massive data").map(_ => 1).getOrElse[Int](List("I'm not integer"))
^
有趣的是,您可以从unit
或getOrElse
返回fold
,这可能会在应用程序中引入错误,除非您注意到单位品味。
scala> val data = Option("massive data").fold() { _ => 1 }
data: Unit = ()
scala> val data = Option("massive data").map(_ => 1).getOrElse()
data: AnyVal = 1
答案 1 :(得分:1)
作为对@ prayagupd答案的反对,06-17 11:58:04.855 29388-29388/? E/SELinux: [DEBUG] get_category: variable seinfo: default sensitivity: NULL, cateogry: NULL
06-17 11:58:04.865 172-172/? E/BufferQueueCore: [Starting kz.pillowz.app] setDefaultMaxBufferCount: setting count to 3, previous is 2
06-17 11:58:04.956 29388-29388/? E/AndroidRuntime: FATAL EXCEPTION: main
Process: kz.pillowz.app, PID: 29388
java.lang.RuntimeException: Unable to instantiate application kz.pillowz.app.App: java.lang.ClassNotFoundException: Didn't find class "kz.pillowz.app.App" on path: DexPathList[[zip file "/data/app/kz.pillowz.app-1/base.apk"],nativeLibraryDirectories=[/data/app/kz.pillowz.app-1/lib/arm, /vendor/lib, /system/lib]]
at android.app.LoadedApk.makeApplication(LoadedApk.java:661)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6027)
at android.app.ActivityThread.access$1700(ActivityThread.java:218)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1805)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:145)
at android.app.ActivityThread.main(ActivityThread.java:7007)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1404)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1199)
Caused by: java.lang.ClassNotFoundException: Didn't find class "kz.pillowz.app.App" on path: DexPathList[[zip file "/data/app/kz.pillowz.app-1/base.apk"],nativeLibraryDirectories=[/data/app/kz.pillowz.app-1/lib/arm, /vendor/lib, /system/lib]]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
at java.lang.ClassLoader.loadClass(ClassLoader.java:469)
at android.app.Instrumentation.newApplication(Instrumentation.java:1003)
at android.app.LoadedApk.makeApplication(LoadedApk.java:651)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6027)
at android.app.ActivityThread.access$1700(ActivityThread.java:218)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1805)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:145)
at android.app.ActivityThread.main(ActivityThread.java:7007)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1404)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1199)
Suppressed: java.lang.ClassNotFoundException: kz.pillowz.app.App
at java.lang.Class.classForName(Native Method)
at java.lang.BootClassLoader.findClass(ClassLoader.java:781)
at java.lang.BootClassLoader.loadClass(ClassLoader.java:841)
at java.lang.ClassLoader.loadClass(ClassLoader.java:504)
... 13 more
Caused by: java.lang.NoClassDefFoundError: Class not found using the boot class loader; no stack available
06-17 11:58:04.986 753-29404/? E/android.os.Debug: ro.product_ship = true
经常邀请你以特定方式弄乱类型。
问题是,根据Scala的规则,只有fold
用于推断 ifEmpty
,然后检查B
是否合适。这意味着使用f
或None
作为Nil
这很常见,会导致他们的单身人士类型被用作ifEmpty
而不是B
,不会无论Option/List[SomeType]
返回什么。
当然,有一些解决方法:明确指定f
,使用B
或Option.empty[SomeType]
代替None: Option[SomeType]
。或者只使用模式匹配。