Добавление функциональности перед вызовом конструктора в дополнительном конструкторе

#scala

#scala

Вопрос:

Возможно ли добавить функциональность перед вызовом конструктора в дополнительном конструкторе в scala?

Допустим, у меня есть класс User, и я хочу получить одну строку — и разделить ее на атрибуты — чтобы отправить их в конструктор:

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

Итак, я знаю, что не могу добавить строку перед отправкой в this, потому что все конструкторы должны вызывать другой конструктор в качестве первого оператора конструктора.

Тогда что я могу сделать вместо этого?

Редактировать:

У меня длинный список атрибутов, поэтому я не хочу повторять line.split(",")

Ответ №1:

Я думаю, что это место, где сопутствующий объект и apply() метод прекрасно вступают в игру:

 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)
 

Затем вы просто создаете свой объект следующим образом:

 val u1 = User("Zorro,33")
 

Кроме того, поскольку вы предоставляете name и age в любом случае, вы можете рассмотреть возможность использования класса case вместо стандартного класса и иметь согласованный способ построения User объектов (без new ключевого слова):

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

Комментарии:

1. вам не нужно создавать класс case исключительно для сокрытия new: используйте private для конструктора и еще один метод apply в companion

Ответ №2:

Уродливое, но рабочее решение # 1:

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

Уродливое, но рабочее решение # 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)
  }
}
 

Который можно использовать как:

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

Вы могли бы использовать apply вместо fromString , но это приведет к путанице (в одном случае вы должны использовать new , в другом вы должны отказаться от него), поэтому я предпочитаю использовать другое имя

Комментарии:

1. Я думаю, что буду использовать ваше второе решение, если нет лучшего… Я вижу, что scala немного разочаровывает

2. Вы можете сделать второе решение красивее с помощью patmat: line.split('.') match { case Array(a, b) => new User(a, b.toInt) }

3. @AlexIv Я предпочитаю val Array(a, b) = line.split(","); new User(a, b.toInt) 🙂

Ответ №3:

Еще одно уродливое решение:

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

Но использование метода сопутствующего объекта, вероятно, лучше.