2011-06-20 7 views
31

En el lenguaje maravilloso, es muy sencillo para comprobar si hay o nullfalse como:¿Cómo comprobar nulo o falso en Scala de forma concisa?

código maravilloso:

def some = getSomething() 
if(some) { 
// do something with some as it is not null or emtpy 

} 

En Groovy si es somenull o es una cadena vacía o es el número cero evaluará etc. a false. ¿Qué método de prueba conciso es similar para null o false en Scala? ¿Cuál es la respuesta simple a esta parte de la pregunta suponiendo que some es simplemente de tipo Java String?

También otro aún mejor método en el maravilloso es:

def str = some?.toString() 

que significa que si some no es null entonces el método toString en some sería invocado en vez de tirar NPE en caso some era null. ¿Qué es similar en Scala?

+0

IMO, la respuesta de Daniel a continuación es la más adecuada. Scala ya tiene mucho soporte para tratar con valores potencialmente vacíos; es mejor aprender y usar eso que crear una nueva sintaxis '?' Con implícitos. – Bill

+1

Una razón para ir con 'null's en lugar de' None's puede ser el rendimiento/uso de la memoria: es posible que prefiera asignar un objeto efímero de la conversión implícita sobre la marcha en lugar de mantener un contenedor 'Option'. Por supuesto, no muchas personas escriben código de alto rendimiento en la JVM, pero aún así, diría que tiene sus casos de uso. – axel22

Respuesta

49

Lo que puede faltar es que una función como getSomething en Scala probablemente no devolvería una cadena nula, vacía o un número cero. Una función que podría devolver un valor significativo o podría no tener como resultado un Option - devolvería Some(meaningfulvalue) o None.

A continuación, puede comprobar esto y manejar el valor significativo con algo como

val some = getSomething() 
some match { 
    case Some(theValue) => doSomethingWith(theValue) 
    case None   => println("Whoops, didn't get anything useful back") 
} 

Así que en lugar de tratar de codificar el valor "fracaso" en el valor de retorno, Scala tiene un apoyo específico para el retorno común" algo significativo o indicar el fracaso "caso.

Habiendo dicho eso, Scala es interoperable con Java y Java devuelve nulos de funciones todo el tiempo. Si getSomething es una función de Java que devuelve nulo, hay un objeto de fábrica que hará que algunos o ninguno salgan del valor devuelto.

Así

val some = Option(getSomething()) 
    some match { 
    case Some(theValue) => doSomethingWith(theValue) 
    case None   => println("Whoops, didn't get anything useful back") 
    } 

... que es bastante simple, reclamo, y no va a NPE en usted.

Las otras respuestas están haciendo cosas interesantes e idiomáticas, pero eso puede ser más de lo que necesita en este momento.

+0

Gracias! Terminé usando su solución, pero siento haber marcado otra solución como solución. – ace

+1

esta es la peor característica de Scala que he encontrado hasta ahora en mi viaje de un mes ... es una paradoja de la ideología "tipo menos" de scala. imo NPE es fácil de arreglar y no puedes confiar en Lang para que haga todo por ti. Incluso con la opción, las personas pueden escribir código que conduzca a NPE. es decir, si en lugar de match case, si alguien acaba de hacer Some (val) .get, entonces todavía obtiene NPE. Tal vez en el futuro esto parezca más útil, pero hasta ahora no he encontrado ningún artículo que explique lo contrario – nir

+0

Continúe, y predigo que va a cambiar de opinión. Por supuesto, puedes provocar un NPE si lo intentas, pero una buena práctica en Scala es no utilizar Option .get, hay muchas formas mejores de hacerlo. –

19

En Scala, las expresiones que describió significan que se invoca un método llamado ? en un objeto llamado some. Regularmente, los objetos no tienen un método llamado ?. Puede crear su propia conversión implícita a un objeto con un método ? que compruebe null ness.

implicit def conversion(x: AnyRef) = new { 
    def ? = x ne null 
} 

La voluntad por encima, en esencia, convertir cualquier objeto en el que se llama al método ? en la expresión en el lado derecho del método conversion (que hace tienen el método ?). Por ejemplo, si usted hace esto:

"".? 

el compilador detectará que un objeto String no tiene un método ?, y volver a escribir en:

conversion("").? 

ilustrado en un intérprete (tenga en cuenta que se puede omitir . métodos cuando se llama a objetos):

scala> implicit def any2hm(x: AnyRef) = new { 
    | def ? = x ne null 
    | } 
any2hm: (x: AnyRef)java.lang.Object{def ?: Boolean} 

scala> val x: String = "!!" 
x: String = "!!" 

scala> x ? 
res0: Boolean = true 

scala> val y: String = null 
y: String = null 

scala> y ? 
res1: Boolean = false 

Así se podría escribir:

if (some ?) { 
    // ... 
} 

O bien, podría crear una conversión implícita en un objeto con un método ? que invoca el método especificado en el objeto si el argumento no es null - hace esto:

scala> implicit def any2hm[T <: AnyRef](x: T) = new { 
    | def ?(f: T => Unit) = if (x ne null) f(x) 
    | } 
any2hm: [T <: AnyRef](x: T)java.lang.Object{def ?(f: (T) => Unit): Unit} 

scala> x ? { println } 
!! 

scala> y ? { println } 

de modo que usted podría entonces escribir:

some ? { _.toString } 

edificio (de forma recursiva) sobre la respuesta del SOC, que puede coincidir con el patrón en x en los ejemplos anteriores para refinar lo ? d depende del tipo de x. : D

+0

esto es bueno! si pudiera explicar los operadores 'eq' y' ne' que forman la esencia de esta respuesta, eso sería aún más útil – asgs

+1

https://stackoverflow.com/questions/10066927/what-is-the-difference-between- a-nenull-and-a-null-in-scala – axel22

6

Puede escribir algunos envoltorios usted mismo o usar un tipo de opción.

Realmente no verificaría null though. Si hay un null en alguna parte, debe arreglarlo y no crear comprobaciones a su alrededor.

Basándose en la cima de la respuesta de axel22:

implicit def any2hm(x: Any) = new { 
    def ? = x match { 
    case null => false 
    case false => false 
    case 0 => false 
    case s: String if s.isEmpty => false 
    case _ => true 
    } 
} 

Editar: Esto parece bien bloquear el compilador o no funciona. Voy a investigar.

+1

El problema es que para declarar 'x' como' Cualquiera' introduce 2 conversiones implícitas para el método '?' en el alcance. – axel22

+0

¿Qué pasa si algo es de tipo Java String? Parece que Scala carece de la concisión de Groovy. – ace

+0

Lo siento pero no estoy siguiendo las dos respuestas, pero gracias por intentarlo. Solo buscaba una solución simple. – ace

33

Bueno, Boolean no puede ser null, a menos que se haya pasado como un parámetro de tipo. La forma de manejar null es convertirlo en Option, y luego usar todas las cosas de Option. Por ejemplo:

Option(some) foreach { s => println(s) } 
Option(some) getOrElse defaultValue 

Desde Scala se escriba de forma estática, una cosa no puede ser "un valor nulo o está vacío o cadena es cero número etc". Puede pasar un Any que puede ser cualquiera de esas cosas, pero luego tendría que coincidir con cada tipo para poder hacer algo útil con él de todos modos. Si te encuentras en esta situación, lo más probable es que no estés haciendo Scala idiomático.

6

Si utiliza extempore's null-safe coalescing operator, entonces usted podría escribir su ejemplo str como

val str = ?:(some)(_.toString)() 

También le permite a la cadena sin tener que preocuparse acerca de null s (por lo tanto "coalescencia"):

val c = ?:(some)(_.toString)(_.length)() 

De Por supuesto, esta respuesta solo aborda la segunda parte de su pregunta.

6

Lo que usted pide es algo en la línea de Safe Navigation Operator (?.) de Groovy, andand gem de Ruby, o accessor variant of the existential operator (?.) de CoffeeScript.Para estos casos, generalmente uso ? método de mi RichOption[T], que se define de la siguiente manera

class RichOption[T](option: Option[T]) { 
    def ?[V](f: T => Option[V]): Option[V] = option match { 
    case Some(v) => f(v) 
    case _ => None 
    } 
} 

implicit def option2RichOption[T](option: Option[T]): RichOption[T] = 
    new RichOption[T](option) 

y se utiliza de la siguiente manera

scala> val xs = None 
xs: None.type = None 

scala> xs.?(_ => Option("gotcha")) 
res1: Option[java.lang.String] = None 

scala> val ys = Some(1) 
ys: Some[Int] = Some(1) 

scala> ys.?(x => Some(x * 2)) 
res2: Option[Int] = Some(2) 
0

El uso de la coincidencia de patrones como se sugiere en un par de respuestas aquí es un buen acercamiento:

val some = Option(getSomething()) 
    some match { 
    case Some(theValue) => doSomethingWith(theValue) 
    case None   => println("Whoops, didn't get anything useful back") 
    } 

Pero, un poco detallado.

prefiero map un Option de la siguiente manera:

Option(getSomething()) map (something -> doSomethingWith(something))

Un forro, breve, claro.

La razón para ello es Opción que se puede ver como some kind of collection - algún copo de nieve especial de una colección que contiene cero elementos o exactamente un elemento de un tipo y como se puede asignar una Lista [A] a una Lista [B ], puede asignar una Opción [A] a una Opción [B]. Esto significa que si su instancia de la Opción [A] está definida, es decir, es Algo [A], el resultado es Algo [B], de lo contrario es Ninguno. ¡Es realmente poderoso!

Cuestiones relacionadas