Evitar el uso de null

La mayoría de los lenguajes tienen una palabra clave u objeto especial para representar que una referencia de objeto es "nada". En Java, es null. En Java, null es una palabra clave, no un objeto, por lo que llamar a cualquier método sobre él es ilegal. Pero esta es una elección confusa para los diseñadores de lenguajes. ¿Por qué devolver una palabra clave cuando el programador espera devolver un objeto?

El tipo Option de Scala

Para hacer que el objetivo de que todo sea un objeto sea más consistente, y también para seguir las prácticas de la programación funcional, Scala te anima a usar el tipo Option cuando las variables y los valores de retorno de funciones puedan no hacer referencia a ningún valor. Cuando no hay valor, usa None, que es una subclase de Option. Si hay un valor que se pueda referenciar, usa Some para contener ese valor. Some también es una subclase de Option. None se declara como un objeto, no como una clase, porque solo necesitamos una instancia de él. De esta manera, se parece un poco a la palabra clave null, pero es un objeto real con métodos.

Ejemplo de aplicación

Los valores de tipo Option se utilizan comúnmente como tipo de retorno de operaciones en colecciones de Scala (List, Map, etc.). Por ejemplo, el método get de Map:

scala> val capitals = Map("France"->"Paris", "Japan"->"Tokyo", "China"->"Beijing")
capitals: scala.collection.immutable.Map[String,String] = Map(France -> Paris, Japan -> Tokyo, China -> Beijing)

scala> capitals get "France"
res0: Option[String] = Some(Paris)

scala> capitals get "North Pole"
res1: Option[String] = None

Option tiene dos subcategorías, Some y None. Cuando el programa devuelve Some, significa que la función te ha dado exitosamente un String, y puedes obtener ese String mediante la función get(). Si el programa devuelve None, significa que no hay ningún String que darte.

Cuando se devuelve None, es decir, no hay ningún String que darte, si insistes en llamar a get() para obtener el String, Scala también te lanzará una excepción NoSuchElementException. También podemos elegir otro método, getOrElse. Este método devuelve el valor correspondiente cuando la Option es una instancia de Some, y devuelve el argumento pasado cuando es una instancia de None. En otras palabras, el argumento pasado a getOrElse es en realidad el valor de retorno por defecto.
scala> capitals get "North Pole" get
warning: there was one feature warning; re-run with -feature for details
java.util.NoSuchElementException: None.get
  at scala.None$.get(Option.scala:347)
  at scala.None$.get(Option.scala:345)
  ... 33 elided

scala> capitals get "France" get
warning: there was one feature warning; re-run with -feature for details
res3: String = Paris

scala> (capitals get "North Pole") getOrElse "Oops"
res7: String = Oops

scala> capitals get "France" getOrElse "Oops"
res8: String = Paris

Mediante la coincidencia de patrones se separan los valores opcionales; si el valor coincidente es Some, se extrae el valor dentro de Some y se asigna a la variable x:

def showCapital(x: Option[String]) = x match {
    case Some(s) => s
    case None => "?"
}

Consejo

Los programas Scala usan Option con mucha frecuencia. En Java se usa null para representar valores nulos, y en el código hay que añadir muchas comprobaciones de la palabra clave null en muchos lugares, de lo contrario es fácil que aparezca NullPointException. Por lo tanto, los programas Java deben preocuparse por qué variables pueden ser null, y la probabilidad de que estas variables sean null es muy baja, pero una vez que ocurre, es difícil averiguar por qué se produce NullPointerException. El tipo Option de Scala puede evitar esta situación, por lo que se recomienda usar el tipo Option para representar algunos valores opcionales en aplicaciones Scala. Al usar el tipo Option, el lector puede ver de un vistazo que el valor de este tipo puede ser None.

En realidad, gracias al tipado estático de Scala, no puedes intentar erróneamente llamar a un método sobre un valor que podría ser null. Aunque en Java este es un error muy fácil de cometer, en Scala no pasa la compilación, porque la falta de comprobación de si una variable es null (lo cual es una práctica común en Java) se convierte en un error de tipo en Scala (no puedes usar Option[String] como String). Por lo tanto, el uso de Option fomenta en gran medida hábitos de programación más flexibles.

Explicación detallada de Option[T]

En Scala, Option[T] es en realidad un contenedor, como un array o una List. Puedes considerarlo como una List que puede tener de cero a un elemento.

Cuando tu Option tiene algo dentro, la longitud de esta List es 1 (es decir, Some), y cuando tu Option no tiene nada, su longitud es 0 (es decir, None).

Bucle for

Si usamos Option como una List normal y recorremos esta Option con un bucle for, si Option es None, entonces el código dentro del bucle for naturalmente no se ejecutará, por lo que logramos no tener que comprobar si Option es None.

scala> val map1 = Map("key1" -> "value1")
map1: scala.collection.immutable.Map[String,String] = Map(key1 -> value1)

scala> val value1 = map1.get("key1")
value1: Option[String] = Some(value1)

scala> val value2 = map1.get("key2")
value2: Option[String] = None

scala> def printContentLength(x: Option[String]) {
     |   for (c <- x){
     |     println(c.length)
     |   }
     | }
printContentLength: (x: Option[String])Unit

scala> printContentLength(value1)
6

scala> printContentLength(value2)

Operación map

Uno de los conceptos centrales en la programación funcional es la transformación, por lo que la mayoría de los lenguajes que admiten programación funcional admiten una acción llamada map(), que te ayuda a transformar el contenido de un contenedor, después de aplicarle ciertas acciones, en otro nuevo contenedor.

Ahora consideremos cómo implementarlo con el método map de Option:length: xxxLa forma de salida:

Primero calcula la longitud de la cadena dentro del contenedor Option.

Luego agrega el texto "length: " delante de la longitud.

Finalmente, recorre el contenedor una vez e imprime lo que hay dentro.

scala> value1.map(_.length).map("length: " + _).foreach(println)
length: 6

scala> value1.map("length: " + _.length).foreach(println)
length: 6

A través de este método de "transformación", podemos lograr el efecto deseado, y también sin tener que hacer la comprobación de "si es None".

Enlace original: https://www.jianshu.com/p/95896d06a94d