Ricerca…


Sintassi

  • class Alcuni [+ T] (valore: T) estende l'opzione [T]

  • oggetto Nessuno estende l'opzione [Nothing]

  • Opzione [T] (valore: T)

    Costruttore per creare un Some(value) o None secondo il valore fornito.

Opzioni come raccolte

Option hanno alcune utili funzioni di ordine superiore che possono essere facilmente comprese visualizzando opzioni come raccolte con zero o un elemento - dove None si comporta come la raccolta vuota, e Some(x) si comporta come una raccolta con un singolo elemento, 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

Utilizzo dell'opzione anziché di null

In Java (e in altre lingue), l'utilizzo di null è un modo comune per indicare che non esiste alcun valore collegato a una variabile di riferimento. In Scala, l'uso Option è preferito rispetto all'utilizzo di null . Option include valori che potrebbero essere null .

None è una sottoclasse di Option racchiude un riferimento null. Some è una sottoclasse di Option racchiude un riferimento non nullo.

Avvolgere un riferimento è facile:

val nothing = Option(null) // None
val something = Option("Aren't options cool?") // Some("Aren't options cool?")

Questo è un codice tipico quando si chiama una libreria Java che potrebbe restituire un riferimento null:

val resource = Option(JavaLib.getResource())
// if null, then resource = None
// else resource = Some(resource)

Se getResource() restituisce un valore null , la resource sarà un oggetto None . Altrimenti sarà un Some(resource) oggetto Some(resource) . Il modo migliore per gestire Option è l'utilizzo di funzioni di ordine superiore disponibili all'interno del tipo di Option . Ad esempio, se si desidera verificare se il proprio valore non è None (simile alla verifica del value == null ), si utilizzerà la funzione isDefined :

val resource: Option[Resource] = Option(JavaLib.getResource())
if (resource.isDefined) {  // resource is `Some(_)` type
  val r: Resource = resource.get
  r.connect()
}

Allo stesso modo, per verificare la presenza di un riferimento null puoi farlo:

val resource: Option[Resource] = Option(JavaLib.getResource())
if (resource.isEmpty) { // resource is `None` type.
  System.out.println("Resource is empty! Cannot connect.")
}

È preferibile che si tratti l'esecuzione condizionale sul valore avvolto di Option (senza utilizzare il metodo 'eccezionale' Option.get ) trattando l' Option come monade e utilizzando 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

Se è richiesta un'istanza Resource (rispetto a un'istanza Option[Resource] , è comunque possibile utilizzare Option per proteggere da valori null. Qui il metodo getOrElse fornisce un valore predefinito:

lazy val defaultResource = new Resource()
val resource: Resource = Option(JavaLib.getResource()).getOrElse(defaultResource)

Il codice Java non gestirà prontamente l' Option di Scala, quindi quando si passano i valori al codice Java è buona norma scartare Option , passando a un valore null o ad un valore predefinito appropriato, ove appropriato:

val resource: Option[Resource] = ???
JavaLib.sendResource(resource.orNull)
JavaLib.sendResource(resource.getOrElse(defaultResource)) // 

Nozioni di base

Option è una struttura di dati che contiene un singolo valore o nessun valore. Option può essere pensata come una raccolta di zero o di un elemento.

L'opzione è una classe astratta con due figli: Some e None .

Some contengono un singolo valore e None non contiene alcun valore.

Option è utile in espressioni che altrimenti utilizzerebbero null per rappresentare la mancanza di un valore concreto. Questo protegge contro una NullPointerException e consente la composizione di molte espressioni che potrebbero non restituire un valore utilizzando combinatori come Map , FlatMap , ecc.

Esempio con la mappa

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] è sigillata e quindi non può essere estesa. Quindi la semantica è stabile e può essere invocata.

Opzioni per le comprensioni

Option s hanno un metodo flatMap . Ciò significa che possono essere utilizzati in a comprensione. In questo modo possiamo sollevare funzioni regolari per lavorare su Option s senza doverle ridefinire.

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)

Quando uno dei valori è un None il risultato finale del calcolo sarà 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

Nota: questo modello si estende più in generale per i concetti denominati Monad s. (Ulteriori informazioni dovrebbero essere disponibili sulle pagine relative a comprensione e Monad s)

In generale non è possibile mescolare diverse monadi in a comprensione. Ma poiché Option può essere facilmente convertita in Iterable , possiamo facilmente mescolare Option s e Iterable chiamando il metodo .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)

Una piccola nota: se avessimo definito la nostra comprensione, l'altro modo di comprensione sarebbe stato compilato poiché la nostra opzione sarebbe stata convertita implicitamente. Per questo motivo è utile aggiungere sempre questa .toIterable (o la funzione corrispondente a seconda della raccolta che si sta utilizzando) per coerenza.



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow