【问题标题】:Scala copy and reflectionScala 复制和反射
【发布时间】:2015-11-04 23:18:43
【问题描述】:

在我的项目中,有很多地方从集合中挑选出对象,复制并更改一些值,然后将其推回集合中。我一直在尝试创建自己的“复制”方法,除了制作副本之外,还给了我一个“差异”对象。换句话说,包含您刚刚放入其中的参数的东西。

然后应该将“差异”对象发送到某个地方进行聚合,以便其他人可以获得自上次以来所有更改的报告,而无需发送实际的整个对象。如果有人这样做,这一切都很简单:

val user = new User(Some(23), true, "arne", None, None, "position", List(), "email", None, false)
val user0 = user.copy(position = "position2")
list ::= user0
val diff =  new Diff[User](Map("position" -> "position2"))

但是,那里有一些重复的工作,我非常希望只使用一种方法,例如:

val (user, diff) = user.copyAndDiff(position = "position")

我无法弄清楚“复制”的参数实际上采用什么形式,但我也可以使用其他形式。

我创建了一个带有 Type 参数的方法,它应该创建一个副本和一个差异。像这样的:

object DiffCopy[Copyable]{
  def apply(original:Copyable, changes:Map[String, Any]){
    original.copy(??uhm..
    original.getAllTheFieldsAndCopyAndOverWriteSomeAccordingToChanges??

我的第一个问题是似乎没有任何方法可以保证原始对象具有我可以重载的“复制”方法。当我想将更改实际分配给新复制对象中的正确字段时,就会出现第二个问题。我试图摆弄反射,并试图找到一种方法来设置名称为字符串的字段的值。在这种情况下,我可以将我的 Diff 保留为一个简单的地图,然后简单地先创建这个 diff-map,然后将其应用于我的对象并将它们发送到他们需要去的地方。

然而,我最终在兔子洞里越陷越深,离我真正想要的也越来越远。我已经到了一个点,我有一个来自任意对象的字段数组,并且可以按名称获取它们,但我无法让它适用于通用类型。所以现在我在这里问是否有人可以就这种情况给我一些建议?

我能得到的最佳答案是,如果有人能告诉我一种将 Map[String, Any] 应用于等效于“复制”方法的简单方法。我相当确定这应该可以实现,但它目前根本超出我的范围......

【问题讨论】:

  • 我并不完全清楚你想要完成什么。更新不可变集合中的不可变对象?然后你应该看看一个镜头库,比如monocle。可以肯定的是,反射不是在 scala 中解决这个问题的惯用方法,
  • 集合并不是问题的一部分。集合可变,对象不可变。我想替换该对象并创建另一个表示所做更改的对象。

标签: scala reflection types copy


【解决方案1】:

有点过于复杂,但解决了你原来的问题。

我能得到的最佳答案是,如果有人能告诉我一种将 Map[String, Any] 应用于等效于“复制”方法的简单方法。我相当确定这应该可以实现,但它目前根本超出我的范围......

  1. 从案例类中获取所有字段到映射。

  2. 使用新值更新地图。

  3. 从新字段映射创建案例类。

问题:

  • 性能低下

我很确定它可以做得更简单......

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

  def getCCParams(cc: Any) =
    (Map[String, Any]() /: cc.getClass.getDeclaredFields) {(a, f) =>
      f.setAccessible(true)
      a + (f.getName -> f.get(cc))
    }

  def enrichCaseClass[T](cc: T, vals : Map[String, Any])(implicit cmf : ClassManifest[T]) = {
    val ctor = cmf.erasure.getConstructors().head
    val params = getCCParams(cc.asInstanceOf[Any]) ++ vals
    val args = cmf.erasure.getDeclaredFields().map( f => params(f.getName).asInstanceOf[Object] )
    ctor.newInstance(args : _*).asInstanceOf[T]
  }

  val j = Person("Jack", 15)
  enrichCaseClass(j, Map("age" -> 18))

【讨论】:

  • 这很好。但你是对的,它的表现不是很好。我想我会坚持记住每次更新对象时都要手动复制和制作差异。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-28
  • 1970-01-01
相关资源
最近更新 更多