web-dev-qa-db-ja.com

Scala:デフォルト値を使用してオブジェクトを初期化する方法

これは例でもっとよく説明されると思います

私は次のケースクラスを持っています

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

姓のデフォルト値も使用するように

または、コンストラクターでそれを実行している場合、null値(またはNone)を検出した場合は、デフォルト値を割り当てることができます。

実際、私はデフォルト値の定義を繰り返さないようにしています。

とにかく、そのようなことを達成するための最も慣用的な方法は何でしょうか?

14
opensas

デフォルト値を使用する場合は、通常、その名前付き引数を省略します。

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

ただし、値をデフォルトにする必要があるかどうかを明示的に知りたいので、マップベースのアイデアをコンストラクターに実装できます。デフォルトを繰り返したくない場合は、次の2つのオプションはどうでしょうか。

オプション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
_
21
dhg

これはEitherのユースケースかもしれないと思います。どちらかを2種類指定できます。

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

これで、LeftまたはRightのどちらがあるかを確認し、必要な引数を使用してコンストラクターを呼び出すことができます。

0
tgr