2016-07-25 1 views
0

Je suis nouveau à Scala, et c'est probablement une question très simple, mais j'ai du mal à comprendre comment faire un objet seulement s'il n'existe pas encore.Scala: Créer un objet seulement s'il n'existe pas encore

Je voudrais interroger une base de données, et savoir si quelque chose est présent, si c'est le cas, stockez-la dans un objet, sinon créez-en une nouvelle. En Java, je sais que ce serait quelque chose comme

PushMessage push = null; 

if(GetFromDatabase() == null) { 
    push = new PushMessaage(param1, param2...); 
} 
else { 
    push = GetFromDatabase(); 
} 

Mais, comment faire cela dans Scala. Lorsque j'essaie de faire la même chose, cela me dit que GetFromDatabase() n'est pas conforme au type attendu Null. De même, j'ai essayé de faire pattern matching et de faire quelque chose comme

val push = GetFromDatabase match { 
    case Some(pushMessage) => pushMessage 
    case None => new PushMessage(param1, param2...) 
} 

Mais cela aussi ne fonctionne pas comme il m'a dit que

Constructor ne peut pas être instancié type prévu, trouvé: Certains [A ], attendu: PushMessage

Alors, comment faire? Toute aide serait vraiment appréciée.

+0

Pourquoi utiliser 'Option' où vous pouvez simplement faire la même chose qu'en Java? 'val m = GetFromDatabase; val push = if (m == null) {nouveau PushMessage ...} sinon m'. –

+0

@Victor Moroz comment est-il plus simple que 'Option (GetFromDatabase) .getOrElse (nouveau PushMessage (params))'? – dk14

Répondre

1

Je suppose que votre GetFromDatabase retourne soit null ou PushMessage, donc afin de faire correspondre modèle correctement, vous devez envelopper dans Option:

val push = Option(GetFromDatabase) match { 
    case Some(pushMessage) => pushMessage 
    case None => new PushMessage(param1, param2...) 
} 

Ou (mauvais style, mais donne une compréhension de la façon dont cela fonctionne):

// Option(null) === None, Option(notNull) === Some(notNull): 
// same as `if (x ne null) Some(x) else None 
val pushMaybe: Option[PushMessage] = Option(GetFromDatabase) 

val push: PushMessage = 
    if (pushMaybe.isEmpty) new PushMessage(param1, param2...) 
    else pushMaybe.get 

Vous pouvez simplifier tout cela avec:

val push = Option(GetFromDatabase).getOrElse(new PushMessage(param1, param2...)) 

P.S. Si GetFromDatabase n'est pas une méthode externe, il est préférable de le réécrire en retour Option[PushMessage] au lieu de PushMessage, quelque chose comme:

def getFromDatabase = { 
    val rs = driver.getResulSet(query) 
    if (!rs.isBeforeFirst()) None else Some(parse(rs)) 
} 
+0

Merci! Existe-t-il un moyen de stocker push à «null» s'il n'y a rien là réellement. Comme avoir 'None => null' pour que je puisse demander si c'est vide ou non? – LivingRobot

+0

vous pouvez utiliser les méthodes '.isEmpty' /' .nonEmpty' de 'Option' pour le vérifier – dk14

+0

Peut-être est-il plus facile de faire correspondre' null' et par défaut au lieu d'encapsuler la valeur ref dans 'Option'? Quel est l'avantage? –

0

Voici une petite démonstration pourquoi « cool » les choses ne sont pas toujours cool. Voyons voir Scala code généré pour deux cas (je les ai fait très simple):

def getMessage: String = null 
val m = getMessage 
val push = if (m == null) new AnyRef else m 

=>

iw.this.m = iw.this.getMessage(); 
iw.this.push = if (iw.this.m().==(null)) 
    new Object() 
else 
    iw.this.m(); 

vs

def getMessage: String = null 
val push = Option(getMessage) match { 
    case Some(x) => x 
    case None => new AnyRef 
} 

=>

iw.this.push = { 
    case <synthetic> val x1: Option = scala.Option.apply(iw.this.getMessage()); 
    case6(){ 
    if (x1.$isInstanceOf[Some]()) 
     { 
     <synthetic> val x2: Some = (x1.$asInstanceOf[Some](): Some); 
     { 
      val x: String = x2.x().$asInstanceOf[String](); 
      matchEnd5(x) 
     } 
     } 
    else 
     case7() 
    }; 
    case7(){ 
    if (scala.None.==(x1)) 
     matchEnd5(new Object()) 
    else 
     case8() 
    }; 
    case8(){ 
    matchEnd5(throw new MatchError(x1)) 
    }; 
    matchEnd5(x: Object){ 
    x 
    } 
}; 

Alors aussi longtemps que nous ne propageons pas null (et nous ne le faisons pas) Je ne vois aucun avantage de la deuxième option.

MISE À JOUR

Comme l'a demandé:

val path = Option(m).getOrElse(new AnyRef) 

=>

final <artifact> private[this] def $anonfun$1(): Object = new Object(); 
... 
iw.this.path = scala.Option.apply($line13.iw.m()).getOrElse({ 
    (() => iw.this.$anonfun$1()) 
}); 

je ne voudrais pas le considérer beaucoup mieux que la deuxième option ci-dessus. getOrElse se cache à peu près la même logique, et nous avons la fonction anonyme puisque getOrElse prend l'argument par nom.

En fait, il ne s'agit pas seulement de sauver des cycles. Construire et déconstruire Option objet dans le but d'éviter local null? Ou sauver une ligne? Je l'accepterais totalement si nous utilisions Option pour le passer plus loin.

+0

scala génère beaucoup d'informations, donc si vous avez besoin d'un langage ou d'une plate-forme performant (bas niveau), JVM n'est peut-être pas un bon choix. Pourriez-vous ajouter javap pour 'getOrElse' pour compléter cette comparaison? – dk14

+0

@ dk14 Fait. C'est plus sur le rasoir d'Occam, que sur les cycles bien que :) –