Scala Language
Classe d'option
Recherche…
Syntaxe
class Some [+ T] (valeur: T) étend Option [T]
objet Aucun étend Option [Nothing]
Option [T] (valeur: T)
Constructeur pour créer soit
Some(value)
ouNone
selon la valeur fournie.
Options en tant que collections
Option
ont des fonctions utiles de niveau supérieur qui peuvent être facilement comprises en affichant les options sous forme de collections avec zéro ou un élément - où None
se comporte comme la collection vide et Some(x)
se comporte comme une collection avec un seul élément, 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
Utilisation de l'option au lieu de null
En Java (et dans d'autres langages), l'utilisation de null
est un moyen courant d'indiquer qu'il n'y a pas de valeur attachée à une variable de référence. Dans Scala, utiliser Option
est préférable à l'utilisation de null
. Option
encapsule les valeurs pouvant être null
.
None
est une sous-classe de Option
enveloppe une référence null. Some
est une sous-classe d' Option
enveloppant une référence non NULL.
Envelopper une référence est facile:
val nothing = Option(null) // None
val something = Option("Aren't options cool?") // Some("Aren't options cool?")
Il s'agit d'un code typique lors de l'appel d'une bibliothèque Java susceptible de renvoyer une référence null:
val resource = Option(JavaLib.getResource())
// if null, then resource = None
// else resource = Some(resource)
Si getResource()
renvoie une valeur null
, la resource
sera un objet None
. Sinon, ce sera un objet Some(resource)
. La manière préférée de traiter une Option
consiste à utiliser les fonctions d'ordre supérieur disponibles dans le type d' Option
. Par exemple, si vous voulez vérifier si votre valeur n'est pas None
(similaire à vérifier si la value == null
), vous utiliseriez la fonction isDefined
:
val resource: Option[Resource] = Option(JavaLib.getResource())
if (resource.isDefined) { // resource is `Some(_)` type
val r: Resource = resource.get
r.connect()
}
De même, pour rechercher une référence null
vous pouvez le faire:
val resource: Option[Resource] = Option(JavaLib.getResource())
if (resource.isEmpty) { // resource is `None` type.
System.out.println("Resource is empty! Cannot connect.")
}
Il est préférable de traiter l'exécution conditionnelle sur la valeur Option.get
d'une Option
(sans utiliser la méthode Option.get
«exceptionnelle») en traitant l' Option
comme une monade et en utilisant 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
Si une instance Resource
est requise (par rapport à une instance Option[Resource]
), vous pouvez toujours utiliser Option
pour vous protéger contre les valeurs NULL. Ici, la méthode getOrElse
fournit une valeur par défaut:
lazy val defaultResource = new Resource()
val resource: Resource = Option(JavaLib.getResource()).getOrElse(defaultResource)
Le code Java ne gèrera pas facilement l’ Option
de Scala, donc lorsqu’il transmet des valeurs au code Java, il est préférable de déballer une Option
, en transmettant null
ou une valeur par défaut sensible, le cas échéant:
val resource: Option[Resource] = ???
JavaLib.sendResource(resource.orNull)
JavaLib.sendResource(resource.getOrElse(defaultResource)) //
Les bases
Une Option
est une structure de données contenant une valeur unique ou aucune valeur. Une Option
peut être considérée comme une collection de zéro ou d'un élément.
Option est une classe abstraite avec deux enfants: Some
et None
.
Some
contiennent une seule valeur et None
ne contient aucune valeur.
Option
est utile dans les expressions qui utiliseraient autrement null
pour représenter l'absence d'une valeur concrète. Cela protège contre une NullPointerException
et permet la composition de nombreuses expressions qui pourraient ne pas renvoyer de valeur en utilisant des combinateurs tels que Map
, FlatMap
, etc.
Exemple avec carte
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]
est scellée et ne peut donc pas être étendue. Par conséquent, sa sémantique est stable et peut être invoquée.
Options pour la compréhension
Option
ont une méthode flatMap
. Cela signifie qu'ils peuvent être utilisés pour la compréhension. De cette manière, nous pouvons lever des fonctions régulières pour travailler sur Option
s sans avoir à les redéfinir.
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)
Lorsque l' une des valeurs est un None
résultat de fin du calcul sera None
aussi bien.
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
Remarque: ce modèle s’étend plus généralement aux concepts appelés Monad
s. (Plus d'informations devraient être disponibles sur les pages relatives à la compréhension et Monad
s)
En général, il n'est pas possible de mélanger différentes monades dans une compréhension. Mais comme Option
peut être facilement convertie en une Iterable
, nous pouvons facilement mélanger Option
s et Iterable
s en appelant la méthode .toIterable
.
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)
Une petite note: si nous avions défini le notre pour la compréhension, la compréhension serait compilée car notre option serait convertie implicitement. Pour cette raison, il est utile de toujours ajouter cette .toIterable
(ou fonction correspondante selon la collection que vous utilisez) pour la cohérence.