Utilización de la opción por todo el lugar se siente un poco incómodo. ¿Estoy haciendo algo mal?

StackOverflow https://stackoverflow.com/questions/2476835

  •  21-09-2019
  •  | 
  •  

Pregunta

Como resultado de los artículos que leí sobre la clase Option que le ayuda a evitar NullPointerException de, empecé a usarlo por todo el lugar. Imaginar algo como esto:

var file:Option[File] = None

y más tarde cuando lo uso:

val actualFile = file.getOrElse(new File("nonexisting"))
if(actualFile.getName.equals("nonexisting")) { // instead of null checking

}
else { // value of file was good

}

Hacer cosas como esta no se siente todo ese "derecho" a mí. También he notado que .get ha convertido en desuso. . Es este tipo de cosas lo que ustedes están haciendo con la opción de también, O voy por el camino equivocado?

¿Fue útil?

Solución

Por lo general, no es una buena idea para volver Option y luego usar getOrElse para producir algún valor centinela que significa "no encontrado". Eso es lo que está diseñado para Option: para significar que no se encuentra un valor

Option realmente demuestra su potencia cuando se utiliza junto con construcciones de programación funcionales como map y foreach. Esto es más potente cuando se trata de múltiples opciones. Por ejemplo, supongamos que yo escribo un método que toma una cadena y me devuelve un archivo, pero sólo si el archivo existe y es un archivo no es un directorio:

import java.io._;
def niceFile1(s: String): File = {
  val f = new File(s);
  if (f.exists && !f.isDirectory) f else null
}
def niceFile2(s: String): Option[File] = {
  val f = new File(s);
  if (f.exists && !f.isDirectory) Some(f) else None
}

Hasta ahora, el uso de null es más fácil - al menos hasta que se olvide que esto podría dar null y se obtiene un NPE. De todos modos, ahora vamos a tratar de usarlo.

def niceFopen1(s: String) = {
  val f = niceFile1(s);
  if (f!=null) new FileInputStream(f) else null;
}
def niceFopen2(s: String) = niceFile2(s).map(f => new FileInputStream(f))

Mira lo que pasó! En el primer caso, tuvimos que hacer pruebas de lógica con la mano y crear variables temporales. Uf! En el segundo caso, map hizo todo el trabajo sucio por nosotros: Ninguno fue mapeado en Ninguno y Some(file) fue asignada a Some(fileinputstream). Fácil!

Pero se pone mejor todavía. Tal vez queremos encontrar el tamaño de un montón de archivos:

def totalSize2(ss: Seq[String]) = {
  (0L /: ss.flatMap(niceFile2)){(sum,f) => sum+f.length}
}

Espera, ¿qué está pasando aquí - ¿qué pasa con toda la None? ¿No tenemos que prestar atención y manejarlos de alguna manera? Bueno, ahí es donde entra en juego flatMap: Reúne todas las respuestas en una sola lista. None es una respuesta de longitud cero, por lo que ignora. Some(f) tiene una respuesta - f - por lo que lo pone en la lista. Luego usamos un pliegue sumar todas las longitudes - ahora que todos los elementos de la lista son válidas. Bastante agradable!

Otros consejos

Es una buena idea para no resuelve el valor de la Option, pero a solicite lógica a lo que es que contiene:

findFile.foreach(process(_))

Básicamente, este procesa un File si uno se encuentra y no hace nada de lo contrario (y es equivalente a la primera comprensión for Thomas' porque for compila a una llamada a foreach). Es una versión más concisa de:

findFile match {
  case Some(f) => process(f)
  case None =>
}

Lo que es más, lo bueno de esto es que se puede cadena operaciones, algo como:

(findLiveFile orElse fileBackupFile orElse findTempFile).foreach(process(_)

En la mayoría de los casos se usaría coincidencia de patrones

file match {
   case Some(f) => { .. } //file is there
   case _ => { .. } //file is not there 
}

Si está interesado sólo en el archivo si está allí se puede utilizar una para la expresión

for(f <- file) { //file is there 
}

A continuación, puede cadena de manifestaciones de trabajar en múltiples niveles en los recipientes

for{ 
  option <- List(Some(1), None, Some(2))
  f <- option
} yield f

res0: List[Int] = List(1, 2)

Alternativamente, puede utilizar IsDefined y obtener:

if(option.isDefined) {
   val x = option.get;
} else {
}

get no está en desuso en Scala 2.8.0.Beta-1.

Esto es una alternativa:

var file:Option[File] = None
// ...
file map (new File(_)) foreach { fh =>
  // ...
}

Sin embargo, si lo que necesita hacer algo si el archivo existe, y algo más si no lo hace, una declaración match es más apropiado:

var file:Option[File] = None
// ...
file map (new File(_)) match {
  case Some(fh) =>
    // ...
  case None =>
    // ...
}

Eso es más o menos lo mismo que una declaración if, pero como ella es el encadenamiento de la naturaleza mejor para cosas como Option, en el que necesito para extraer un valor también.

Medio repitiendo lo que todos dicen, pero creo que hay por lo menos 4 diferentes situaciones que se encontrará cuando se tiene un var anulable como esto:

1. El archivo no debe ser nulo

load(file.get())

producirá una excepción cuando intenta utilizar realmente el archivo. Fallar rápido, yay!

2. El archivo es nulo, pero en este caso tenemos un defecto sensata:

load(file.getOrElse(new File("/defaultFile")))

3. Dos ramas de la lógica en función de si existe el archivo:

  file match {
    case Some(f) => { .. } //file is there
    case _ => { .. } //file is not there 
  }

4. No-op si el archivo es nulo, también conocido como silencio falle. No creo que éste es muy a menudo preferible para mí en la vida real:

for (f <- file) {
//do some stuff
}

O, tal vez con mayor claridad?

if (f.isDefined) {
  //do some stuff
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top