web-dev-qa-db-fra.com

Comment vérifier de manière concise les valeurs nulles ou fausses dans Scala?

En langage Groovy, il est très simple de vérifier null ou false comme:

code groovy:

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

}

Dans Groovy si some est null ou est une chaîne vide ou est zéro nombre etc. sera évalué à false. Quelle est la méthode concise similaire pour tester null ou false dans Scala? Quelle est la réponse simple à cette partie de la question en supposant que some est simplement du type Java String?

Une autre méthode encore meilleure en groovy est:

def str = some?.toString()

ce qui signifie que si some n'est pas null, alors la méthode toString sur some serait invoquée au lieu de lancer NPE dans le cas où some serait null. Qu'est-ce qui est similaire à Scala?

31
ace

Ce qui vous manque peut-être, c'est qu'une fonction comme getSomething dans Scala ne renverrait probablement pas null, chaîne vide ou nombre zéro. Une fonction qui pourrait ou non renvoyer une valeur significative aurait pour retour Option - elle renverrait Some(meaningfulvalue) ou None

Vous pouvez alors vérifier cela et gérer la valeur significative avec quelque chose comme

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

Ainsi, au lieu d'essayer de coder la valeur "échec" dans la valeur de retour, Scala prend en charge de manière spécifique le cas commun "renvoyer quelque chose de significatif ou d'indiquer un échec".

Cela dit, Scala est interopérable avec Java, et Java renvoie les valeurs NULL des fonctions tout le temps. Si getSomething est une fonction Java qui renvoie la valeur null, il existe un objet fabrique qui créera Some ou None à partir de la valeur renvoyée.

Alors 

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

... ce qui est assez simple, je prétends, et n'ira pas NPE sur vous.

Les autres réponses font des choses intéressantes et idiomatiques, mais c'est peut-être plus que ce dont vous avez besoin maintenant.

51

Boolean ne peut pas être null, à moins d’être passé en paramètre de type. La manière de gérer null consiste à le convertir en un Option, puis à utiliser tous les éléments Option. Par exemple:

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

Puisque Scala est de type statique, une chose ne peut pas être " une chaîne nulle ou une chaîne vide, ou est un nombre nul, etc. ". Vous pouvez passer une Any qui peut être n'importe laquelle de ces choses, mais vous devrez alors faire correspondre chaque type pour pouvoir faire quoi que ce soit d’utile avec elle de toute façon. Si vous vous trouvez dans cette situation, vous ne faites probablement pas de Scala idiomatique.

35
Daniel C. Sobral

Dans Scala, les expressions que vous avez décrites signifient qu'une méthode appelée ? est appelée sur un objet appelé some. Régulièrement, les objets n’ont pas de méthode appelée ?. Vous pouvez créer votre propre conversion implicite en un objet avec une méthode ? qui vérifie nullness.

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

En gros, tout ce qui précède convertit tout objet sur lequel vous appelez la méthode ? en expression du côté droit de la méthode conversion (qui a a la méthode ?). Par exemple, si vous faites cela:

"".?

le compilateur détecte qu'un objet String n'a pas de méthode ? et le réécrit dans:

conversion("").?

Illustré dans un interpréteur (notez que vous pouvez omettre . lorsque vous appelez des méthodes sur des objets):

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

Pour que vous puissiez écrire:

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

Ou vous pouvez créer une conversion implicite en un objet avec une méthode ? qui appelle la méthode spécifiée sur l'objet si l'argument n'est pas null - procédez comme suit:

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 }

afin que vous puissiez ensuite écrire:

some ? { _.toString }

En vous appuyant (de manière récursive) sur la réponse de soc, vous pouvez appliquer une correspondance de motif à x dans les exemples ci-dessus pour préciser ce que ? fait en fonction du type de x. :RÉ

19
axel22

Si vous utilisez l'opérateur de coalescence null-safe de extempore , vous pouvez écrire votre exemple str sous la forme suivante: 

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

Cela vous permet également de chaîner sans vous soucier de nulls (donc "coalescing"):

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

Bien entendu, cette réponse ne concerne que la deuxième partie de votre question.

6
Aaron Novstrup

Ce que vous demandez, c'est quelque chose dans la ligne de Safe Navigation Operator (?.) de Groovy, et gem de Ruby, ou variante d'accesseur de l'opérateur existentiel (?.) de CoffeeScript . Dans de tels cas, j'utilise généralement la méthode ? de mon RichOption[T], qui est définie comme suit

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)

et utilisé comme suit

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)
6
Volkan Yazıcı

Vous pouvez écrire vous-même un wrapper ou utiliser un type Option.

Je ne voudrais vraiment pas vérifier pour null cependant. S'il y a une null quelque part, vous devriez la corriger et ne pas construire de contrôles autour d'elle.

S'appuyant sur la réponse d'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
  }
}

Edit: Cela semble planter le compilateur ou ne fonctionne pas. Je vais enquêter.

6
soc