2010-12-17 84 views
14

Scala的Option類有一個orNull方法,其簽名如下所示。請解釋使用Option的orNull方法

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

我對隱含的東西感到困惑。請有人解釋一下如何使用它,理想情況下用一個例子?

+6

唉,源代碼使用'空<: 2010-12-17 10:49:23

回答

26
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是獲得來自一些回來的路|無成語Java的價值|空成語(這是當然的,壞)。現在只有AnyRef值(類的實例)可以接受空值。

所以我們會喜歡的是def orNull[A >: Null] = ....。但是A已經設定好了,我們不想限制它在特質的定義中。因此,或者Null期望證明A是可爲空的類型。這個證據的形式是一個隱含的變量(因此名字爲'ev')

<:<[Null, A1]可以寫成Null <:< A1看到它是這樣的,它類似於'空'<: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 
+4

看到這樣的事情,實際上很難不愛Scala。 – Madoc 2010-12-17 06:15:29

+2

圖書館編寫者走了很長的路要把所有東西都模塊化,並且工作得很好;缺點是簽名對於局外人來說通常看起來很奇怪,而且錯誤信息是無法理解的。一個好的提示可能是忽略它,只是閱讀文檔。 :-) – 2010-12-17 08:42:20

4

請記住,在Scala中,基本類型和引用類型是統一的 - 但只有引用類型可以爲空。隱式只允許編譯器確認A1是引用類型。

+0

好的。你會給我一個它的用法的例子嗎? – David 2010-12-17 04:39:14

+0

取消那個; IttayD提供了一個例子。 「編程斯卡拉」這一節似乎相關:http://programming-scala.labs.oreilly.com/ch12.html#NothingAndNull。 – David 2010-12-17 05:09:04

5

orNull目的是在保證兼容性首先是用Java編寫的Option。儘管在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) 
+1

只是爲了使其更加明確:這僅適用於引用類型(AnyRef的子類型),但不適用於值類型(AnyVal的子類型),因爲它們沒有空值。 – 2010-12-17 08:20:39

+0

那麼,如果getInitialText代替getInitialText,返回Long?假設你想把這個很長的數字傳遞給Java,或者如果它沒有被定義,則爲null。 – David 2010-12-17 19:37:52

+1

然後,您需要使用類型歸屬,顯式指定'java.lang.Long'而不是'scala.Long':'val initialText:Option [java.lang.Long] = getInitialLong' – 2010-12-17 20:56:27

2

要理解爲什麼它是有用的,IttayD provided a nice explanation

那麼,我們所希望的是高清 orNull[A >: Null] = .....但A是 已經設置,我們不想 限制它在 特質的定義。因此,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,因爲NullString的一個子類型,並且類<:<From類型參數被定義爲逆變)。如上所述,考慮類型約束最直觀的方式是將其讀取爲類型綁定(即將其讀取爲空值<:Int)。 Null不符合Int,並且<沒有隱式值:< [Null,Int]。另一方面,Null確實符合String,編譯器將查找隱式參數。

順便說一下,這裏是另一個related answer

0

回覆:這是如何使用的 - 我們發現這個有用的一個地方是在處理java API映射時,其中null很常見,例如,在jdbc準備好的語句中爲可空的sql列。所述Option人內部模型字段可以被映射:

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

相反的更詳細的:

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