Scala Language
Optionsklasse
Suche…
Syntax
Klasse Einige [+ T] (Wert: T) erweitert Option [T]
Objekt Keine erweitert Option [Nichts]
Option [T] (Wert: T)
Konstruktor, um je nach angegebenem
Some(value)
entweder einenSome(value)
oderNone
zu erstellen.
Optionen als Sammlungen
Option
haben einige nützliche Funktionen höherer Ordnung, die leicht verstanden werden können, wenn Optionen als Sammlungen mit null oder einem Element None
verhält sich None
wie die leere Sammlung und Some(x)
wie eine Sammlung mit einem einzelnen Element, x
.
val option: Option[String] = ???
option.map(_.trim) // None if option is None, Some(s.trim) if Some(s)
option.foreach(println) // prints the string if it exists, does nothing otherwise
option.forall(_.length > 4) // true if None or if Some(s) and s.length > 4
option.exists(_.length > 4) // true if Some(s) and s.length > 4
option.toList // returns an actual list
Option anstelle von Null verwenden
In Java (und anderen Sprachen) ist die Verwendung von null
eine häufige Methode, um anzuzeigen, dass an eine Referenzvariable kein Wert angehängt ist. In Scala wird die Verwendung von Option
Verwendung von null
vorgezogen. Option
umschließt Werte, die möglicherweise null
.
None
ist eine Unterklasse von Option
die eine Nullreferenz einschließt. Some
sind eine Unterklasse von Option
die eine nicht-null-Referenz einschließt.
Eine Referenz einwickeln ist einfach:
val nothing = Option(null) // None
val something = Option("Aren't options cool?") // Some("Aren't options cool?")
Dies ist ein typischer Code, wenn eine Java-Bibliothek aufgerufen wird, die möglicherweise eine Nullreferenz zurückgibt:
val resource = Option(JavaLib.getResource())
// if null, then resource = None
// else resource = Some(resource)
Wenn getResource()
einen null
zurückgibt, ist die resource
ein None
Objekt. Andernfalls handelt es sich um ein Some(resource)
Objekt Some(resource)
. Die bevorzugte Methode zum Umgang mit einer Option
ist die Verwendung von Funktionen höherer Ordnung innerhalb des Option
. Wenn Sie beispielsweise prüfen möchten, ob Ihr Wert nicht None
(ähnlich wie beim Überprüfen, ob value == null
), würden Sie die isDefined
Funktion verwenden:
val resource: Option[Resource] = Option(JavaLib.getResource())
if (resource.isDefined) { // resource is `Some(_)` type
val r: Resource = resource.get
r.connect()
}
Um nach einer null
suchen, können Sie Folgendes tun:
val resource: Option[Resource] = Option(JavaLib.getResource())
if (resource.isEmpty) { // resource is `None` type.
System.out.println("Resource is empty! Cannot connect.")
}
Es wird bevorzugt, dass Sie die bedingte Ausführung des umschlossenen Werts einer Option
(ohne die Option.get
Methode "außergewöhnlich" Option.get
verwenden), indem Sie die Option
als Monade behandeln und foreach
:
val resource: Option[Resource] = Option(JavaLib.getResource())
resource foreach (r => r.connect())
// if r is defined, then r.connect() is run
// if r is empty, then it does nothing
Wenn eine Resource
erforderlich ist (im Gegensatz zu einer Option[Resource]
-Instanz), können Sie Option
zum Schutz vor NULL-Werten verwenden. Hier liefert die getOrElse
Methode einen Standardwert:
lazy val defaultResource = new Resource()
val resource: Resource = Option(JavaLib.getResource()).getOrElse(defaultResource)
Java-Code kann Scalas Option
nicht ohne weiteres verarbeiten. Wenn Sie Werte an Java-Code übergeben, ist es eine gute Form, eine Option
zu entpacken, wobei null
oder ein sinnvoller Standardwert übergeben wird:
val resource: Option[Resource] = ???
JavaLib.sendResource(resource.orNull)
JavaLib.sendResource(resource.getOrElse(defaultResource)) //
Grundlagen
Eine Option
ist eine Datenstruktur, die entweder einen einzelnen oder keinen Wert enthält. Eine Option
kann als Sammlung von null oder einem Element betrachtet werden.
Option ist eine abstrakte Klasse mit zwei Kindern: Some
und None
.
Some
enthält einen einzelnen Wert und " None
enthält keinen Wert.
Option
ist in Ausdrücken nützlich, die andernfalls null
, um das Fehlen eines konkreten Werts darzustellen. Dies schützt vor einer NullPointerException
und ermöglicht die Zusammensetzung vieler Ausdrücke, die möglicherweise keinen Wert mit Kombinatoren wie Map
, FlatMap
usw. zurückgeben.
Beispiel mit Map
val countries = Map(
"USA" -> "Washington",
"UK" -> "London",
"Germany" -> "Berlin",
"Netherlands" -> "Amsterdam",
"Japan" -> "Tokyo"
)
println(countries.get("USA")) // Some(Washington)
println(countries.get("France")) // None
println(countries.get("USA").get) // Washington
println(countries.get("France").get) // Error: NoSuchElementException
println(countries.get("USA").getOrElse("Nope")) // Washington
println(countries.get("France").getOrElse("Nope")) // Nope
Option[A]
ist versiegelt und kann daher nicht erweitert werden. Daher ist die Semantik stabil und verlässlich.
Optionen für Verständnis
Option
haben eine flatMap
Methode. Dies bedeutet, dass sie für ein Verständnis verwendet werden können. Auf diese Weise können wir reguläre Funktionen zur Bearbeitung von Option
ohne sie neu definieren zu müssen.
val firstOption: Option[Int] = Option(1)
val secondOption: Option[Int] = Option(2)
val myResult = for {
firstValue <- firstOption
secondValue <- secondOption
} yield firstValue + secondValue
// myResult: Option[Int] = Some(3)
Wenn einer der Werte " None
ist, lautet das Endergebnis der Berechnung " None
.
val firstOption: Option[Int] = Option(1)
val secondOption: Option[Int] = None
val myResult = for {
firstValue <- firstOption
secondValue <- secondOption
} yield firstValue + secondValue
// myResult: Option[Int] = None
Hinweis: Dieses Muster erstreckt sich allgemeiner auf Konzepte, die Monad
. (Weitere Informationen sollten auf Seiten verfügbar sein, die sich auf Verständnis und Monad
's beziehen.)
Im Allgemeinen ist es nicht möglich, verschiedene Monaden zum Verständnis zu mischen. Da Option
jedoch leicht in eine Iterable
konvertiert werden kann, können Sie Option
und Iterable
s einfach mischen, indem Sie die .toIterable
Methode aufrufen.
val option: Option[Int] = Option(1)
val iterable: Iterable[Int] = Iterable(2, 3, 4, 5)
// does NOT compile since we cannot mix Monads in a for comprehension
// val myResult = for {
// optionValue <- option
// iterableValue <- iterable
//} yield optionValue + iterableValue
// It does compile when adding a .toIterable on the option
val myResult = for {
optionValue <- option.toIterable
iterableValue <- iterable
} yield optionValue + iterableValue
// myResult: Iterable[Int] = List(2, 3, 4, 5)
Eine kleine Anmerkung: Wenn wir unser Verständnis für das Verständnis definiert hätten, würde sich das Gegenteil um das Verständnis herum ausrechnen, da unsere Option implizit konvertiert würde. Aus diesem Grund ist es sinnvoll, diese .toIterable
Funktion (oder die entsprechende Funktion, abhängig von der verwendeten Sammlung) immer aus .toIterable
hinzuzufügen.