griffon vulture
griffon vulture

Reputation: 6764

Adding functionality before calling constructor in extra constructor

Is it possible to add functionality before calling constructor in extra constructor in scala ?

Lets say, I have class User, and want to get one string - and to split it into attributes - to send them to the constructor:

class User(val name: String, val age: Int){
    def this(line: String) = {
       val attrs = line.split(",") //This line is leading an error - what can I do instead
       this(attrs(0), attrs(1).toInt)
    }
}

So I know I'm not able to add a line before sending to this, because all constructors need to call another constructor as the first statement of the constructor.

Then what can I do instead?

Edit:

I have a long list of attributes, so I don't want to repeat line.split(",")

Upvotes: 1

Views: 56

Answers (3)

Jasper-M
Jasper-M

Reputation: 15086

Another ugly solution:

class User(line: String) {
  def this(name: String, age: Int) = this(s"$name,$age")

  val (name, age) = {
    val Array(nameStr,ageStr) = line.split(",")
    (nameStr,ageStr.toInt)
  }
}

But using a method of the companion object is probably better.

Upvotes: 1

Norbert Radyk
Norbert Radyk

Reputation: 2618

I think this is a place where companion object and apply() method come nicely into play:

object User {
  def apply(line: String): User = {
    val attrs = line.split(",")
    new User(attrs(0), attrs(1).toInt)
  }
}

class User(val name: String, val age: Int)

Then you just create your object the following way:

val u1 = User("Zorro,33")

Also since you're exposing name and age anyway, you might consider using case class instead of standard class and have consistent way of constructing User objects (without new keyword):

object User {
  def apply(line: String): User = {
    val attrs = line.split(",")
    new User(attrs(0), attrs(1).toInt)
  }
}

case class User(name: String, age: Int)

val u1 = User("Zorro,33")

val u2 = User("Zorro", "33")

Upvotes: 2

om-nom-nom
om-nom-nom

Reputation: 62835

Ugly, but working solution#1:

class User(val name: String, val age: Int){
    def this(line: String) = {
       this(line.split(",")(0), line.split(",")(1).toInt)
    }
}

Ugly, but working solution#2:

class User(val name: String, val age: Int)
object User {
  def fromString(line: String) = {
     val attrs = line.split(",")
     new User(attrs(0), attrs(1).toInt)
  }
}

Which can be used as:

val johny = User.fromString("johny,35")

You could use apply in place of fromString, but this will lead to a confusion (in one case you have to use new, in the other you have to drop it) so I prefer to use different name

Upvotes: 2

Related Questions