3

How in Scala I can define local variable in primary constructor?

I need to solve this exercise from Scala for the impatient book:

Write a class Person with a primary constructor that accepts a string containing a first name, a space, and a last name, such as new Person("Fred Smith"). Supply read-only properties firstName and lastName. Should the primary constructor parameter be a var, a val, or a plain parameter? Why?

And for now my solution looks like this:

class Person(firstLast: String) {
  private[this] val firstLastAsArr = firstLast.trim.split(" ")

  val firstName = firstLastAsArr (0)
  val lastName = firstLastAsArr (1)
}

How I can restrict firstLastAsArr variable visibility to primary constructor scope (now it have class scope)?

1

5 Answers 5

5

One solution is to initialize firstName and lastName at once, thereby allowing to turn firstLastAsArr into a local temporary value inside your initialization block:

class Person(firstLast: String) {
  val (firstName, lastName) = {
    val firstLastAsArr = firstLast.trim.split(" ")
    (firstLastAsArr(0), firstLastAsArr(1))
  }
}
Sign up to request clarification or add additional context in comments.

5 Comments

firstLastAsArr(1) will actually contain the separator, i.e., " "
@MalteSchwerhoff whoa? "John Doe".trim.split(" ") results to Array(John, Doe)
@om-nom-nom Interesting. I tried it locally as a Scala 2.10.1 script (gist.github.com/anonymous/5821751), but ideone.com/4b27Dr shows the behaviour as well with Scala 2.10.0.
@MalteSchwerhoff that is because you have two spaces in "John Doe" and (,1) is the void between them
@om-nom-nom Oh boy, missing that is fairly embarrassing. Thanks!
4

It is not a general answer, but in this particular way you may write:

  val Array(firstName, lastName) = firstLast.trim.split(" ")

Comments

2

You don't strictly need the intermediate variable:

class Person(firstLast: String) {
  val (firstName, lastName) =
    firstLast.trim.split(" ") match {case Array(first, last) => (first, last)}
}

However, if your transformation from firstLast to firstName and lastName grows a big longer, for example, because you check that there is exactly one first and one last name, then I would encapsulate the whole splitting-business in a dedicated method:

class Person(firstLast: String) {
  val (firstName, lastName) = split(firstLast)

  private def split(firstLast: String): (String, String) = {
    val firstLastAsArr = firstLast.trim.split(" ")
    ...
    (first, last)
  }
}

Comments

1

Pattern matching in constructor works just fine, but you should consider moving such logic from constructor to factory method:

case class Person(firstName: String, lastName: String)
object Person{
  def apply(firstLast: String) = {
    val firstLastAsArr = firstLast.trim.split(" ")
    new Person(firstLastAsArr(0), firstLastAsArr(1))
  }
}

val p = Person("My Title")

2 Comments

@pagoda_5b: this is really not a correct answer in this case, but I can't add formated code sample to comment. I think the one who need to initialize 2 fields from 1 constructor parameter should use a factory method. SO is not about homework.
+1 for the factory method solution -- I'd also go for this pattern, as it avoids creating temporary tuples.
0

Pattern maching in primary constructor works well

  class Person(_fullName:String) {
    val (firstName, lastName) =  _fullName.split(" ") match {
      case Array(x:String, y:String, _*) => (x,y)
      case _ => (null,null)
    }
  }

See my github for full answer https://github.com/BasileDuPlessis/scala-for-the-impatient/blob/master/src/main/scala/com/basile/scala/ch05/Ex07.scala

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.