【问题标题】:Please explain use of Option's orNull method请解释使用 Option 的 orNull 方法
【发布时间】:2010-12-17 04:06:45
【问题描述】:

Scala 的 Option 类有一个orNull 方法,其签名如下所示。

orNull [A1 >: A](implicit ev : <:<[Null, A1]) : A1

我对隐含的东西感到困惑。有人能解释一下它是如何使用的吗,最好是一个例子?

【问题讨论】:

  • 唉,源代码使用Null &lt;:&lt; A1,这使它更直观。不过,Scaladoc 从编译器获取类型签名,所以这些语法细节早已被他们抛弃了。不过,我正在与一位 scaladoc 提交者谈论它。让我们看看这是否得到改进。

标签: api scala scala-java-interop


【解决方案1】:
scala> Some(1).orNull
<console>:10: error: could not find implicit value for parameter ev: <:<[Null,Int]
       Some(1).orNull
               ^
scala> (None : Option[Int]).orNull
<console>:10: error: could not find implicit value for parameter ev: <:<[Null,Int]
       (None : Option[Int]).orNull

scala> Some("hi").orNull
res21: java.lang.String = hi

scala> Some(null : String).orNull
res22: String = null

scala> (None : Option[String]).orNull
res23: String = null

解释隐含的事情:orNull 是一种从 Some|None 习语返回到 Java 的 value|null 习语的方法(当然,这很糟糕)。现在只有 AnyRef 值(类的实例)可以接受空值。

所以我们想要的是def orNull[A &gt;: Null] = ....。但是 A 已经设置好了,我们不想在 trait 的定义中限制它。因此, orNull 期望 A 是可空类型的证据。该证据采用隐式变量的形式(因此名称为“ev”)

&lt;:&lt;[Null, A1]可以写成Null &lt;:&lt; A1这样看,和'Null <: a1 predef>conforms 的隐式值的方法中。

我认为这里不严格要求使用 A1 是因为 orNull 使用 getOrElse (这里给定的默认值可以是 A 的超类型)

scala> class Wrapper[A](option: Option[A]) {
     | def orNull(implicit ev: Null <:< A): A = if(option.isEmpty) null else option.get
     | }
defined class Wrapper

scala> new Wrapper(Some("hi")).orNull
res18: java.lang.String = hi

【讨论】:

  • 看到这样的东西,真的很难不喜欢 Scala。
  • 库的编写者在使所有东西都模块化并且运行良好方面做了很长的路要走;缺点是签名对于外人来说通常看起来非常奇怪,并且错误消息难以理解。一个好的提示可能是忽略它并阅读文档。 :-)
【解决方案2】:

orNull 的目的首先是确保Option 与Java 的兼容性。尽管在 Scala 中不鼓励使用 null,但某些接口可能期望获得可为空的引用。

orNull 有一个简单的实现:

def orNull[A1 >: A](implicit ev: Null <:< A1): A1 = this getOrElse null

据此,null 不仅会返回盒装空值 (Some(null)),还会返回 None(例如,如果您调用 None.get,则会引发异常)。

隐式参数检查,如果装箱的值可以为空。

可以在comments to orNull中找到好的用法示例:

val initialText: Option[String] = getInitialText
val textField = new JComponent(initialText.orNull,20)

【讨论】:

  • 只是为了更明确一点:这仅适用于引用类型(AnyRef 的子类型),但不适用于值类型(AnyVal 的子类型),因为它们没有空值。
  • 如果你有 getInitialNum 而不是 getInitialText,返回 Long 怎么办?假设您想将该长数字传递给 Java,如果未定义,则为 null。
  • 那你需要使用类型归属,明确指定java.lang.Long而不是scala.Long:val initialText: Option[java.lang.Long] = getInitialLong
【解决方案3】:

请记住,在 Scala 中,原始类型和引用类型是统一的——但只有引用类型可以为空。隐式只允许编译器确认 A1 是一个引用类型。

【讨论】:

【解决方案4】:

要了解它为什么有用,IttayD provided a nice explanation:

所以我们想要的是 def orNull[A &gt;: Null] = ..... 但是 A 是 已经设置,我们不想 将其限制在定义中 特征。因此, orNull 期望一个 证明 A 是可空类型的证据。 这个证据的形式是 隐式变量(因此得名 'ev')

总之,当您希望泛型类(例如Option)上的方法(例如orNull)具有比类本身(例如@987654327)更具体的约束(例如Null &lt;: A &lt;: Any)时,类型约束很有用@)。

这是另一个未内置于语言中的“功能”,但由于隐式参数和类型参数的方差注释而免费提供。要理解这一点,请看&lt;:&lt;的定义:

// from Predef      
sealed abstract class <:<[-From, +To] extends (From => To)
implicit def conforms[A]: A <:< A = new (A <:< A) {def apply(x: A) = x}

对于

scala> Some(1).orNull
<console>:10: error: could not find implicit value for parameter ev: <:<[Null,Int]
       Some(1).orNull

编译器查找&lt;:&lt;[Null, Int] 类型的隐式值,并将找到方法def conforms[A]: A &lt;:&lt; A。所以必须有一个A,其中&lt;:&lt;[A, A] 符合&lt;:&lt;[Null, Int]。没有 A 成立,因此编译器会抱怨缺少隐式值。

然而,对于

scala> Some("hi").orNull
res21: java.lang.String = hi

我们很幸运。现在,编译器尝试找到一个&lt;:&lt;[A, A] 符合&lt;:&lt;[Null, String]A。这适用于A = String,因为NullString 的子类型,并且&lt;:&lt; 类的From 类型参数被定义为逆变的)。

如前所述,考虑类型约束的最直观方式是将其视为类型绑定(即,将其读取为 Null <: int>Null 不符合 Int 并且 <: int>Null 确实符合String,编译器会找到隐式参数。

顺便说一句,这是另一个related answer

【讨论】:

    【解决方案5】:

    Re : 'how' 是如何使用的 - 我们发现这很有用的一个地方是在处理 null 很常见的 java api 映射时,例如在 jdbc 上将语句准备为可为空的 sql 列。 Optional 内部模型字段可以映射:

    stmt.setDate("field", myModel.myDateField.orNull)
    

    而不是更冗长:

    stmt.setDate("field", myModel.myDateField.getOrElse(null))
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-19
      • 2013-03-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多