3

次のようなケースクラスがある場合:

case class Foo(x: String, y: String, z: String)

そして、次のような 2 つのインスタンスがあります。

Foo("x1","y1","z1")
Foo("x2","y2","z2")

結果が次のようになるように、フィールド z を除いて、インスタンス 1 をインスタンス 2 にマージすることは可能ですか?

Foo("x1","y1","z2")

私のユースケースは、Scala API を介してバックボーン アプリに JSON オブジェクトを渡すことです。バックボーン アプリは、保存/更新できるように同じ構造の JSON を返します。これらの JSON オブジェクトは、簡単に Scala を操作できるようにケース クラスとして解析されます。ただし、一部のフィールドはクライアント側で更新しないでください (creationDate など)。今のところ、私は手動でマージを行っていますが、強化されたコピー機能のような、より一般的なソリューションが必要です。

私が欲しいのは次のようなものです:

instanceFromDB.updateWith(instanceFromBackbone, excludeFields = "creationDate" )

しかし、私はそれがタイプセーフであることを望みます:)

編集: 私のケースクラスにはさらに多くのフィールドがあり、明示的にマージしないように言わない限り、デフォルトの動作でフィールドをマージしたいと思います。

4

4 に答える 4

4

あなたが望むものはすでにそこにあります。別の方法で問題に取り組む必要があるだけです。

case class Bar(x: String, y: String)
val b1 = Bar("old", "tired")
val b2 = Bar("new", "fresh")

特に言及されていないものすべてがb2必要な場合は、からコピーする必要がありますb2b1保持したいものは何でも明示的に言及できます:

def keepY(b1: Bar, b2: Bar) = b2.copy(y = b1.y)

scala> keepY(b1, b2)
res1: Bar = Bar(new,tired)

同じケース クラスの 2 つのインスタンス間でコピーを行っており、フィールドがデフォルトのように不変である限り、これで目的が達成されます。

于 2013-01-12T11:18:58.787 に答える
3
case class Foo(x: String, y: String, z: String)

Foo("old_x", "old_y", "old_z")
// res0: Foo = Foo(old_x,old_y,old_z)

Foo("new_x", "new_y", "new_z")
// res1: Foo = Foo(new_x,new_y,new_z)

// use copy() ...
res0.copy(res1.x, res1.y)
// res2: Foo = Foo(new_x,new_y,old_z)

// ... with by-name parameters
res0.copy(y = res1.y)
// res3: Foo = Foo(old_x,new_y,old_z)
于 2013-01-12T00:49:59.857 に答える
3

カリー化することにより、copy メソッドによる自動コピーからクラス パラメータを除外できます。

case class Person(name: String, age: Int)(val create: Long, val id: Int)

これにより、クライアントが設定する通常の値フィールドと特別なフィールドが明確になります。特別なフィールドを提供することを誤って忘れることはありません。

あるインスタンスから値フィールドを取得し、別のインスタンスから特別なフィールドを取得するユースケースでは、デフォルトの引数または元の特別なメンバーのいずれかを使用して反射的にコピーを呼び出します。

import scala.reflect._
import scala.reflect.runtime.{ currentMirror => cm }
import scala.reflect.runtime.universe._
import System.{ currentTimeMillis => now }

case class Person(name: String, age: Int = 18)(val create: Long = now, val id: Int = Person.nextId) {
  require(name != null)
  require(age >= 18)
}
object Person {
  private val ns = new java.util.concurrent.atomic.AtomicInteger
  def nextId = ns.getAndIncrement()
}

object Test extends App {

  /** Copy of value with non-defaulting args from model. */
  implicit class Copier[A: ClassTag : TypeTag](val value: A) {
    def copyFrom(model: A): A = {
      val valueMirror = cm reflect value
      val modelMirror = cm reflect model
      val name = "copy"
      val copy = (typeOf[A] member TermName(name)).asMethod

      // either defarg or default val for type of p
      def valueFor(p: Symbol, i: Int): Any = {
        val defarg = typeOf[A] member TermName(s"$name$$default$$${i+1}")
        if (defarg != NoSymbol) {
          println(s"default $defarg")
          (valueMirror reflectMethod defarg.asMethod)()
        } else {
          println(s"def val for $p")
          val pmethod = typeOf[A] member p.name
          if (pmethod != NoSymbol) (modelMirror reflectMethod pmethod.asMethod)()
          else throw new RuntimeException("No $p on model")
        }
      }
      val args = (for (ps <- copy.paramss; p <- ps) yield p).zipWithIndex map (p => valueFor(p._1,p._2))
      (valueMirror reflectMethod copy)(args: _*).asInstanceOf[A]
    }
  }
  val customer  = Person("Bob")()
  val updated   = Person("Bobby", 37)(id = -1)
  val merged    = updated.copyFrom(customer)
  assert(merged.create == customer.create)
  assert(merged.id == customer.id)
}
于 2013-01-12T13:41:52.597 に答える
1
case class Foo(x: String, y: String, z: String)

val foo1 = Foo("x1", "y1", "z1")
val foo2 = Foo("x2", "y2", "z2")

val mergedFoo = foo1.copy(z = foo2.z) // Foo("x1", "y1", "z2")

後で次のように変更した場合Foo:

case class Foo(w: String, x: String, y: String, z: String)

変更を行う必要はありません。明示的に:

val foo1 = Foo("w1", "x1", "y1", "z1")
val foo2 = Foo("w2", "x2", "y2", "z2")

val mergedFoo = foo1.copy(z = foo2.z) // Foo("w1", "x1", "y1", "z2")
于 2013-01-12T01:57:02.627 に答える