Utilisation de l'option dans tous les sens se sent un peu maladroit. Est-ce que je fais quelque chose de mal?

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

  •  21-09-2019
  •  | 
  •  

Question

En conséquence des articles que je lis sur la classe Option qui vous aide à éviter NullPointerException de, j'ai commencé à l'utiliser partout. Imaginez quelque chose comme ceci:

var file:Option[File] = None

et plus tard quand je l'utilise:

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

}
else { // value of file was good

}

Faire des choses comme ça ne se sent pas du tout « droit » à moi. J'ai aussi remarqué que .get est devenu obsolète. . Est-ce genre de choses que les gars que vous faites avec l'option est trop, ou vais-je dans le mauvais sens?

Était-ce utile?

La solution

Il est généralement pas une bonne idée de revenir Option puis utiliser getOrElse pour produire une valeur sentinelle qui signifie « introuvable ». C'est ce que Option est conçu pour: pour signifier qu'une valeur ne se trouve pas

Option montre vraiment sa puissance lorsqu'il est utilisé conjointement avec des constructions de programmation fonctionnels comme map et foreach. Ceci est le plus puissant quand vous utilisez plusieurs options. Par exemple, supposons que j'écris une méthode qui prend une chaîne et me donne un fichier, mais seulement si le fichier existe et est un fichier pas un répertoire:

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
}

Jusqu'à présent, l'utilisation null est plus facile - au moins jusqu'à ce que vous oubliez que cela pourrait vous donner null et vous obtenez un NPE. Quoi qu'il en soit, nous allons maintenant essayer de l'utiliser.

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))

Regardez ce qui est arrivé! Dans le premier cas, nous avons dû faire des tests logiques manuellement et créer des variables temporaires. Pouah! Dans le second cas, map a fait tout le sale boulot pour nous: Aucun a été mis en correspondance Aucun, et Some(file) a été mis en correspondance avec Some(fileinputstream). Facile!

Mais il y a mieux encore. Peut-être que nous voulons trouver la taille d'un tas de fichiers:

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

Attendez, ce qui se passe ici - ce sur tous les None? Ne nous devons prêter attention et de les gérer en quelque sorte? Eh bien, c'est là flatMap vient: il réunit toutes les réponses dans une liste unique. None est une réponse de longueur zéro, il l'ignore. Some(f) a une réponse - f - il le place dans la liste. Ensuite, nous utilisons un pli pour additionner toutes les longueurs - maintenant que tous les éléments de la liste sont valides. Pretty nice!

Autres conseils

Il est une bonne idée de ne résoudra pas la valeur du Option, mais appliquer logique à ce qui est, il contient:

findFile.foreach(process(_))

En gros, cela traite un File si l'on se trouve et ne fait rien autrement (et est équivalent à la première compréhension de for Thomas parce for compile un appel à foreach). Il est une version plus concise:

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

De plus, la grande chose à ce sujet est que vous pouvez chaîne opérations, quelque chose comme:

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

Dans la plupart des cas, vous devez utiliser pattern matching

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

Si vous n'êtes intéressé que le fichier s'il est là, vous pouvez utiliser une expression pour

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

Vous pouvez ensuite la chaîne pour les expressions de travailler sur plusieurs niveaux sur les conteneurs

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

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

Sinon, vous pouvez utiliser isDefined et obtenir:

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

get n'est pas dépréciée à Scala 2.8.0.Beta-1.

Voici une alternative:

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

Cependant, si vous avez besoin de faire quelque chose si le fichier existe, et autre chose si elle ne le fait pas, une déclaration de match est plus approprié:

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

C'est à peu près la même chose que d'une déclaration de if, mais je l'aime est enchaînant mieux la nature des choses comme Option, où je veux extraire une valeur aussi bien.

La plupart du temps ce que tout le monde reformulant dit, mais je pense qu'il ya au moins 4 situations différentes, vous rencontrerez lorsque vous avez un annulable var comme ceci:

1. Le fichier ne doit pas être nulle

load(file.get())

jetteront une exception lorsque vous essayez d'utiliser réellement le fichier. Fail rapide, youpi!

2. Le fichier est nul, mais dans ce cas nous avons un défaut raisonnable:

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

3. Deux branches de la logique selon que le fichier existe:

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

4. Non-op si le fichier est nul, alias échouer en mode silencieux. Je ne pense pas que celui-ci est très souvent préférable pour moi dans la vie réelle:

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

Ou, peut-être plus clairement?

if (f.isDefined) {
  //do some stuff
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top