Buscar..


Sintaxis

  • clase Algunos [+ T] (valor: T) extiende la opción [T]

  • objeto Ninguno extiende la Opción [Nada]

  • Opción [T] (valor: T)

    El constructor creará un Some(value) o None según sea apropiado para el valor proporcionado.

Opciones como colecciones

Option s tienen algunas funciones útiles de orden superior que pueden entenderse fácilmente al ver las opciones como colecciones con cero o un solo elemento , donde None comporta como la colección vacía, y Some(x) comportan como una colección con un solo 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

Usando Opción en lugar de Null

En Java (y otros lenguajes), usar null es una forma común de indicar que no hay un valor asociado a una variable de referencia. En Scala, se prefiere usar Option en lugar de usar null . Option ajusta valores que pueden ser null .

None es una subclase de Option ajusta una referencia nula. Some es una subclase de Option envuelve una referencia no nula.

Envolver una referencia es fácil:

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

Este es un código típico al llamar a una biblioteca de Java que podría devolver una referencia nula:

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

Si getResource() devuelve un valor null , el resource será un objeto None . De lo contrario, será un objeto Some(resource) . La forma preferida de manejar una Option es usar funciones de orden superior disponibles dentro del tipo de Option . Por ejemplo, si desea verificar si su valor no es None (similar a verificar si el value == null ), usaría la función isDefined :

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

De manera similar, para verificar una referencia null puede hacer esto:

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

Es preferible que trate la ejecución condicional en el valor envuelto de una Option (sin usar el método 'excepcional' Option.get ) tratando la Option como una mónada y usando 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 se requiere una instancia de Resource lugar de una instancia de Option[Resource] ), aún puede usar la Option para protegerse contra valores nulos. Aquí el método getOrElse proporciona un valor predeterminado:

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

El código Java no manejará fácilmente la Option de Scala, por lo que al pasar valores al código Java es una buena forma de desenvolver una Option , pasar un valor null o un valor predeterminado razonable cuando sea apropiado:

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

Lo esencial

Una Option es una estructura de datos que contiene un solo valor o ningún valor en absoluto. Una Option puede considerarse como colecciones de cero o uno de los elementos.

La opción es una clase abstracta con dos hijos: Some y None .

Some contienen un solo valor, y None no contiene ningún valor.

Option es útil en expresiones que de lo contrario usarían null para representar la falta de un valor concreto. Esto protege contra una NullPointerException y permite la composición de muchas expresiones que pueden no devolver un valor utilizando combinadores como Map , FlatMap , etc.

Ejemplo con mapa

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á sellada y, por lo tanto, no puede extenderse. Por lo tanto, su semántica es estable y se puede confiar en ella.

Opciones en para las comprensiones

Option s tienen un método flatMap . Esto significa que pueden ser utilizados en una comprensión. De esta manera podemos levantar funciones regulares para trabajar en las Option s sin tener que redefinirlas.

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)

Cuando uno de los valores es None el resultado final del cálculo también será 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: este patrón se extiende más generalmente para los conceptos llamados Monad s. (Más información debe estar disponible en las páginas relacionadas con las comprensiones y Monad )

En general, no es posible mezclar diferentes mónadas en una comprensión. Pero dado que la Option se puede convertir fácilmente en un Iterable , podemos combinar fácilmente las Option sy Iterable llamando al método .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 pequeña nota: si hubiéramos definido nuestra comprensión para la otra manera, la comprensión se compilaría ya que nuestra opción se convertiría implícitamente. Por esa razón, es útil agregar siempre este .toIterable (o la función correspondiente según la colección que esté usando) para .toIterable coherencia.



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow