Scala 的 Option 类有一个orNull
方法,其签名如下所示。
orNull [A1 >: A](implicit ev : <:<[Null, A1]) : A1
我对隐含的事情感到困惑。有人可以解释一下它是如何使用的,最好举个例子吗?
Scala 的 Option 类有一个orNull
方法,其签名如下所示。
orNull [A1 >: A](implicit ev : <:<[Null, A1]) : A1
我对隐含的事情感到困惑。有人可以解释一下它是如何使用的,最好举个例子吗?
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 >: Null] = ....
. 但是 A 已经设置好了,我们不想在 trait 的定义中限制它。因此, orNull 期望 A 是可空类型的证据。该证据采用隐式变量的形式(因此名称为“ev”)
<:<[Null, A1]
可以写成这样Null <:< 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
orNull
目的首先是确保Option
与 Java 的兼容性。尽管null
在 Scala 中不鼓励使用 of,但某些接口可能期望获得可为空的引用。
orNull
有一个简单的实现:
def orNull[A1 >: A](implicit ev: Null <:< A1): A1 = this getOrElse null
据此,null
不仅会为装箱的空值 ( Some(null)
) 返回,而且还会为None
(例如,如果您调用None.get
,将引发异常)返回。
隐式参数检查,如果装箱的值可以为空。
好的用法示例可以在以下评论中orNull
找到:
val initialText: Option[String] = getInitialText
val textField = new JComponent(initialText.orNull,20)
请记住,在 Scala 中,原始类型和引用类型是统一的——但只有引用类型可以为空。隐式只允许编译器确认 A1 是引用类型。
为了理解它为什么有用,IttayD 提供了一个很好的解释:
所以我们想要的是 def
orNull[A >: Null] = .....
但是 A 已经设置好了,我们不想在 trait 的定义中限制它。因此, orNull 期望 A 是可空类型的证据。该证据采用隐式变量的形式(因此名称为“ev”)
总而言之,当您希望orNull
泛型类(例如)上的方法(例如)具有比类本身(例如)Option
更具体的约束(例如)时,类型约束很有用。Null <: A <: Any
A <: Any
这是另一个未内置于语言中的“功能”,但由于隐式参数和类型参数的方差注释而免费提供。要理解这一点,请查看 的定义<:<
:
// 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
编译器查找类型的隐式值<:<[Null, Int]
并找到方法def conforms[A]: A <:< A
。所以必须有一个A
符合<:<[A, A]
的<:<[Null, Int]
。没有A
这适用,因此编译器会抱怨缺少隐式值。
然而,对于
scala> Some("hi").orNull
res21: java.lang.String = hi
我们很幸运。现在,编译器试图找到一个A
符合<:<[A, A]
. <:<[Null, String]
这适用于A = String
,因为Null
它是 的子类型,String
并且From
类的类型参数<:<
被定义为逆变的)。
如前所述,考虑类型约束的最直观的方式是像类型绑定一样读取它(即读取为 Null <: Int)。Null
<:<[Null, Int]不符合Int
并且没有隐含值。另一方面,Null
确实符合String
并且编译器会找到隐式参数。
顺便说一句,这是另一个相关的答案。
Re : 'how' is this used - one place we are finding this useful is when dealing with java api mappings where null
is commonplace, e.g. on jdbc prepared statements to nullable sql columns. The Option
al internal model fields can be mapped:
stmt.setDate("field", myModel.myDateField.orNull)
Instead of the more verbose:
stmt.setDate("field", myModel.myDateField.getOrElse(null))