2010-12-17 11 views
14

Scalas Option-Klasse hat eine orNull-Methode, deren Signatur unten gezeigt wird.Erläutern Sie bitte die Verwendung der Option orNull

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

Ich bin verwirrt durch die implizite Sache. Würde jemand bitte erklären, wie es genutzt werden kann, am besten mit einem Beispiel?

+6

Ach, der Quellcode verwendet 'Null <:

Antwort

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 

die implizite Sache zu erklären: orNull ist eine Möglichkeit, von der einige immer wieder | Kein Idiom auf Java Wert | null Idiom (was natürlich ist, schlecht). Jetzt können nur AnyRef-Werte (Instanzen von Klassen) einen Nullwert akzeptieren.

Was uns also gefallen hätte, ist def orNull[A >: Null] = ..... Aber A ist bereits festgelegt und wir wollen es nicht in der Definition des Merkmals einschränken. Daher erwartet orNull einen Hinweis, dass A ein Nullwerttyp ist. Dieser Nachweis in Form einer impliziten Variable ist (daher der Name ‚ev‘)

<:<[Null, A1] als Null <:< A1 geschrieben wird es so zu sehen, ist es ähnlich wie ‚Null <: A1‘. <: < ist in Predef definiert sowie die Methode, die den impliziten Wert conforms angibt.

ich denke, die Verwendung von A1 ist hier nicht unbedingt erforderlich und ist, weil orNull verwendet getOrElse (wo die im Falle des Ausfalls ein Supertyp A sein kann)

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

Wenn man solche Sachen sieht, ist es wirklich schwer Scala nicht zu lieben. – Madoc

+2

Die Autoren der Bibliothek gehen einen langen Weg, um alles modular zu machen und gut zu funktionieren; der Nachteil ist, dass die Signaturen für den Außenseiter oft absolut komisch aussehen und die Fehlermeldungen unverständlich sind. Ein guter Tipp könnte sein, es zu ignorieren und nur die Dokumentation zu lesen. :-) –

4

Beachten Sie, dass in Scala primitive Typen und Referenztypen vereinheitlicht sind - aber nur Referenztypen sind nullfähig. Das Implizit erlaubt dem Compiler einfach zu bestätigen, dass A1 ein Referenztyp ist.

+0

OK. Würden Sie mir ein Beispiel für seine Verwendung geben? – David

+0

Abbrechen das; IttayD hat ein Beispiel gegeben. Dieser Abschnitt von "Scala programmieren" scheint relevant zu sein: http://programming-scala.labs.oreilly.com/ch12.html#NothingAndNull. – David

5

orNull Zweck ist in erster Linie die Kompatibilität bei der Gewährleistung von Option mit Java. Obwohl die Verwendung von null in Scala nicht empfohlen wird, können einige Schnittstellen davon ausgehen, NULL-Werte zu erhalten.

orNull hat eine einfache Implementierung:

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

dies nach, wird null für boxed nulls nicht nur zurückgeschickt werden (Some(null)), sondern auch für None (zB wenn Sie None.get anrufen, wird Ausnahme ausgelöst werden).

Implizite Parameterüberprüfungen, wenn der eingerahmte Wert nullfähig ist.

Gute Anwendungsbeispiel kann direkt im comments to orNull finden:

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

Nur um dies deutlicher zu machen: Dies funktioniert nur für Referenztypen (Subtypes von AnyRef), aber nicht für Werttypen (Subtyp von AnyVal), da sie keinen Nullwert haben. –

+0

Was wäre also, wenn Sie anstelle von getInitialText getInitialNum haben und Long zurückgeben? Angenommen, Sie möchten diese lange Zahl an Java übergeben oder null, wenn sie nicht definiert wurde. – David

+1

Dann müssen Sie type-Zuschreibung verwenden, explizite Angabe von 'java.lang.Long' anstelle von' scala.Long': 'val initialText: Option [java.lang.Long] = getInitialLong' –

2

Um zu verstehen, warum es sinnvoll ist, IttayD provided a nice explanation:

Was würden wir gern haben, ist def orNull[A >: Null] = ..... Aber A ist bereits festgelegt und wir wollen nicht beschränken Sie es in der Definition der Eigenschaft. Daher erwartet orNull einen Hinweis, dass A ein Nullable Typ ist. Dieser Nachweis ist in Form einer implizite Variable (daher der Name ‚ev‘)

Zusammengefasst sind Typ Einschränkungen nützlich, wenn Sie Methoden wollen (zB orNull) auf einer generischen Klasse (zB Option) mit spezifischeren Randbedingungen (zB Null <: A <: Any) als auf der Klasse selbst (zB A <: Any).

Dies ist ein weiteres "Feature", das nicht in die Sprache integriert ist, sondern dank impliziter Parameter und Varianzanmerkungen von Typparametern kostenlos zur Verfügung steht. Um dies zu verstehen, Blick auf die Definition von <:<:

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

Für

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

der Compiler sucht nach einem impliziten Wert vom Typ <:<[Null, Int] und das Verfahren def conforms[A]: A <:< A finden. Es muss also eine A sein, für die <:<[Null, Int] entspricht. Es gibt keine A für die dies gilt und der Compiler wird sich über den fehlenden impliziten Wert beschweren.

jedoch für

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

haben wir Glück. Nun versucht der Compiler, eine A zu finden, für die <:<[A, A]<:<[Null, String] entspricht. Dies funktioniert für A = String, weil Null ist ein Untertyp von String und der From Typ Parameter der Klasse <:< ist als kontravariante definiert). Wie erwähnt, ist die intuitivste Art, über Typabhängigkeiten nachzudenken, das Lesen derselben wie eine Typgrenze (d.h. Lesen als Null <: Int). Null entspricht nicht Int und es gibt keinen impliziten Wert für <: < [Null, Int]. Auf der anderen Seite entspricht NullString und der Compiler findet den impliziten Parameter.

Übrigens, hier ist ein weiterer related answer.

0

Re: "Wie" ist das verwendet - eine Stelle, die wir finden, dass diese nützlich ist, wenn es um Java-API-Mappings handelt, wo null ist üblich, z. auf jdbc vorbereitete Anweisungen zu NULL-fähigen SQL-Spalten. Die Option al internen Modellfelder können abgebildet werden:

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

Statt der ausführlicheren:

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