Scala: как инициализировать объект, используя значения по умолчанию

Я думаю, это будет лучше объяснить на примере

У меня есть следующий класс case

case class Person(name: String = "no name", surname: String = "no surname")

И я хочу сделать общую функцию для ее заполнения, например, из сообщения json, в котором могут быть указаны не все поля.

Я знаю, что для использования значений по умолчанию простой ответ — не передавать их конструктору, но если у меня есть несколько полей, которые могут отображаться или не отображаться в json, мне придется использовать огромное предложение переключения, охватывающее все возможные комбинации отсутствующие параметры. В этом случае, после прочтения json, я должен позаботиться о том, чтобы имя и фамилия присутствовали, ни имени, ни фамилии, ни имени, ни фамилии... (Ну и дела, надеюсь, я ясно выразился).

Чтобы быть более точным, я пытаюсь разработать функцию, которая позволяет мне создать человека из следующих значений json, используя значения по умолчанию, когда отсутствует какой-либо параметр.

{ "name": "john", "surname": "doe" }
{ "surname": "doe" }
{ "name": "john" }
{ }

Вот почему я ищу более общий способ справиться с этим.

(Я покажу некоторый псевдокод, чтобы дать представление о том, чего я пытаюсь достичь)

Я думал о чем-то вроде:

val p = Person(name= "new person name", surname= Unit)

И в этом случае фамилия должна получить значение по умолчанию

Или что-то вроде

val p = Person( Map( "name" -> "new person name" ) _* )

Так что он также принимает значение по умолчанию для фамилии

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

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

В любом случае, какой самый идиоматический способ добиться такого?


person opensas    schedule 06.05.2012    source источник


Ответы (2)


Если вы хотите, чтобы использовалось значение по умолчанию, вы обычно просто пропускаете этот именованный аргумент:

scala> val p = Person(name = "new person name")
p: Person = Person(new person name,no surname)

Но, поскольку вы хотите явно знать, должно ли значение быть по умолчанию или нет, вы можете реализовать свою идею на основе карты в конструкторе. Если вы не хотите повторять значения по умолчанию, как насчет этих двух вариантов:

Вариант 1: внешние константы для значений по умолчанию

Установите значения по умолчанию извне. Используйте их как в основном конструкторе, так и в конструкторе на основе карты.

val nameDefault = "no name"
val surnameDefault = "no surname"

case class Person(name: String = nameDefault, surname: String = surnameDefault) {
  def this(m: Map[String, String]) =
    this(m.getOrElse("name", nameDefault), m.getOrElse("surname", surnameDefault))
}

Использование:

new Person(name = "new person name", surname = "new person surname")
new Person(Map("name" -> "new person name"))
new Person(name = "new person name")

Вариант 2: опциональный альтернативный конструктор

Вы можете найти это немного чище, поскольку оно не полагается на внешние константы. Единственным недостатком здесь является то, что если вы хотите построить только с некоторыми параметрами, вы должны обернуть каждый из них в Some().

case class Person(name: String, surname: String) {
  def this(name: Option[String] = None, surname: Option[String] = None) =
    this(name.getOrElse("no name"), surname.getOrElse("no surname"))

  def this(m: Map[String, String]) = this(m.get("name"), m.get("surname"))
}

Использование:

new Person(name = "new person name", surname = "new person surname")
new Person(Map("name" -> "new person name"))
new Person(name = Some("new person name"))
new Person(name = "new person name") // can't do this
person dhg    schedule 06.05.2012
comment
Спасибо, dhg, я это понимаю, но мне нужна общая функция для загрузки значений, которых может не быть, я отредактировал вопрос, чтобы прояснить проблему. - person opensas; 06.05.2012
comment
@opensas, теперь я вижу. Я обновил два варианта, которые могут быть тем, что вы ищете. - person dhg; 06.05.2012
comment
отличный ответ, я думаю, что предпочитаю первый подход, устанавливая константы в объекте-компаньоне Person, просто чтобы сделать их немного менее внешними... - person opensas; 06.05.2012
comment
+1 За вариант №1. Я думаю, что вариант № 2 - это Option злоупотребление. - person Ken Bloom; 06.05.2012
comment
+1 и за вариант №1, творческое использование конструктора alt с параметром карты. json или параметры сервлета отлично подходят здесь, спасибо - person virtualeyes; 07.05.2012

Я думаю, что это может быть прецедентом для Либо. Либо можно указать два типа.

val name: Eiter[String, Unit] = Left("name")
val surname: Either[String, Unit] = Right( () )

Теперь вы можете проверить, есть ли у вас Left или Right, и вызвать конструктор с нужными аргументами.

person tgr    schedule 06.05.2012
comment
Я пытаюсь избежать вызова конструктора с каждой возможной комбинацией присутствующих/отсутствующих параметров... Я не знаю, следил ли я за вами... - person opensas; 06.05.2012