2 votes

Scala: initialiser les champs avec des instances créées ultérieurement

Suppose I have these two classes:

class Person (val name: String, val surname: String)

class Family (val nameFamily: String, val members: Set[Person])

Now in the main method instantiate two classes as follows:

val F1 = new Family ("Red", Set(P1, P2))
val P1 = new Person ("John", "Smith")
val P2 = new Person ("Luis", "Smith")

The main method allows me to enter the family members before their instantiation. I want this for my model. But if I enter the members before their creation, when I go to write:

println(F1.members)

I returns a Set (null).

If you write in the main people first, like this:

val P1 = new Person ("John", "Smith")
val P2 = new Person ("Luis", "Smith")  
val F1 = new Family ("Red", Set(P1, P2))

I have no problem.

But I want to write instances in any order and in the end run a validation of family.

I can solve this problem. I mean, I can initialize my fields with instances created later.

Excuse me for the bad English translation.

UPLOAD #1

I have implemented a domain in Scala, I create instances of the domain with a DSL. My DSL allows me to instantiate classes in mixed order. For example, I create a Component, then add to this Component some Type. Then I create the Type I have added to Component. In the main method I can do it. As the last statement of the main I put validation. When starting validation, Type in the Component does not find anything, because these are instantiated later. This problem can be resolved only in the main with lazy? Or is there a solution at the domain level.

7voto

senia Points 22960

Vous pouvez utiliser lazy val:

scala> :paste
// Entering paste mode (ctrl-D to finish)

case class Person (val name: String, val surname: String)
case class Family (val nameFamily: String, val members: Set[Person])

lazy val f1 = new Family ("Red", Set(p1, p2))
lazy val p1 = new Person ("John", "Smith")
lazy val p2 = new Person ("Luis", "Smith")

// Exiting paste mode, now interpreting.

defined class Person
defined class Family
f1: Family = 
p1: Person = 
p2: Person = 

scala> f1
res0: Family = Family(Red,Set(Person(John,Smith), Person(Luis,Smith)))

Si vous voulez définir une valeur quelque part après la création de l'objet une seule fois, vous devriez utiliser Promise à partir de Scala 2.10:

import concurrent.{Promise, Future}

case class Person (val name: String, val surname: String)
class Family (val nameFamily: String) {
  private val membersPromice = Promise[Set[Person]]
  def setMembers(m: Set[Person]) { membersPromice.success(m) }
  val membersFuture = membersPromice.future
  def members = membersFuture.value.map(_.get)
  override def toString() = "Family(" + nameFamily + ", " + members.getOrElse("") + ")"
}

Utilisation:

scala> val f = new Family("red")
f: Family = Family(red, )

scala> val (p1, p2) = (Person("John", "Smith"), Person("Luis", "Smith"))
p1: Person = Person(John,Smith)
p2: Person = Person(Luis,Smith)

scala> f.setMembers(Set(p1, p2))

scala> f
res1: Family = Family(red, Set(Person(John,Smith), Person(Luis,Smith)))

scala> f.setMembers(Set(p1, p2))
java.lang.IllegalStateException: Promise already completed.

Vous pouvez utiliser le modèle de construction sécurisé par le type:

case class Person (val name: String, val surname: String)
case class Family (val nameFamily: String, val members: Set[Person])

sealed abstract class TBool
class TTrue extends TBool
class TFalse extends TBool
class FamilyBuilder[WithName <: TBool, WithMembers <: TBool](val nameFamily: Option[String], val members: Option[Set[Person]]) {
  def withName(nameFamily: String)(implicit i: WithName =:= TFalse) = new  FamilyBuilder[TTrue, WithMembers](Some(nameFamily), members)
  def withMembers(members: Set[Person])(implicit i: WithMembers =:= TFalse) = new  FamilyBuilder[WithName, TTrue](nameFamily, Some(members))
  def create()(implicit i1: WithName =:= TTrue, i2: WithMembers =:= TTrue) = Family(nameFamily.get, members.get)
}
object FamilyBuilder {
  def apply() = new FamilyBuilder[TFalse, TFalse](None, None)
}

Utilisation:

scala> FamilyBuilder() withName "Red" withMembers Set(p1, p2) create()
res1: Family = Family(Red,Set(Person(John,Smith), Person(Luis,Smith)))

scala> FamilyBuilder() withMembers Set(p1, p2) withName "Red" create()
res2: Family = Family(Red,Set(Person(John,Smith), Person(Luis,Smith)))

scala> FamilyBuilder() withName "Red" create()
:1: error: Cannot prove that TFalse =:= TTrue.
              FamilyBuilder() withName "Red" create()
                                             ^

1voto

pagoda_5b Points 3909

Il suffit de rendre les variables des personnes en mode paresseux

val F1 = new Family("Red", Set(P1, P2))
lazy val P1 = new Person("John", "Smith")
lazy val P2 = new Person("Luis", "Smith")

scala> println(F1.members)
Set($line1.$read$$iw$$iw$Person@185623a7, $line1.$read$$iw$$iw$Person@3f3eb56c)

Modifié

Vous pouvez définir les membres de la famille en mode paresseux dans la classe elle-même

class Family(val nameFamily: String, memberSet: => Set[Person]) {
    lazy val members= memberSet
}

Ainsi, l'attribut members est évalué uniquement sur demande

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X