【问题标题】:Scala reference to 'tupled' generically from case classScala 对“元组”的引用通常来自案例类
【发布时间】:2014-10-01 19:13:13
【问题描述】:

我正在尝试编写一个样板函数,它可以采用一对通用案例类对象并对它们的 vals 集执行一些操作,然后返回案例类的新实例。

在更详细的层面上,这不是这个问题的预期重点,我正在执行的操作是压缩两个两个 val 列表,并有选择地决定通过 List 将元组的哪个项目放入新实例中[布尔] 表示左侧或右侧对象的标志。

我已经写好了大部分概念,到目前为止,我的研究结果是,我的策略是将案例类对象转换为 val 列表(使用 Product.productIterator),然后最终转换为将 vals 放入一个元组 (SO reference) 以馈入 Function.tupled (SO reference)(可用于案例类)。

trait PermissionMask[B <: Product] {
    val permissionMask: List[Boolean]

    def mergePermissibleEdits(userObject: B, trustedObject: B) = {
      val possibleValues = (userObject.productIterator.toList) zip (trustedObject.productIterator.toList)
      val valuesWithFlags = possibleValues zip permissionMask

      val mergedObjectList = valuesWithFlags map { case ((userValue, trustedValue), userEditable) => if (userEditable) userValue else trustedValue }

      //TODO: Not possible, I don't have a direct reference to the case class since it's generic, not sure how to get the reference
      B.tupled(mergedObjectList) //I have implicit conversions to tuple1-22 from list
    }
  }

如果我没有使用泛型并且知道具体的案例类,我可以简单地调用 SomeCaseClass.tupled(mergedObjectList)。但是,由于它是通用的,我对如何(如果可能的话)进行相同的调用很模糊。

【问题讨论】:

  • 这种事情一般来说并不容易,Shapeless已经为你做了很多工作。
  • 请注意,如果您的案例类只有一个元素,那么apply 方法是Function1,它没有tupled 函数,2+ 有元组。

标签: scala


【解决方案1】:

如上所述,您不能使用tupled,因为它不适用于所有案例类apply 方法。这是一个相当老套的版本,但可能“可行”。这采用第一个构造函数并将合并的参数列表应用于它。 警告,这是非常可疑且极易破坏的,我不会在生产中使用它

  def mergePermissibleEdits[B <: Product : ClassTag](userObject: B, trustedObject: B, permissionMask: List[Boolean]) = {
    val possibleValues = userObject.productIterator.toList.map(_.asInstanceOf[AnyRef]) zip trustedObject.productIterator.toList.map(_.asInstanceOf[AnyRef])
    val valuesWithFlags = possibleValues zip permissionMask

    val mergedObjectList = valuesWithFlags map { case ((userValue, trustedValue), userEditable) => if (userEditable) userValue else trustedValue}

    implicitly[ClassTag[B]].runtimeClass.getConstructors.head.newInstance(mergedObjectList: _*).asInstanceOf[B]
  }

  case class Id(id1: Int, id2: String, id3: Double)

  println(mergePermissibleEdits(Id(1, "test", 3.0), Id(4, "works", 6.0), List(true, false, true)))
  //prints Id(1,works,3.0)

【讨论】:

  • 谢谢。您能否扩展您对认真使用此代码的担忧?主要是基于易碎性(例如如果 B、userObject 或trustedObject 的运行时类型不匹配似乎很难防止)还是有进一步的担忧?
  • @Rich 不能保证permissionMask 的大小与possibleValues 的大小相同,这也使用它为B 找到的第一个构造函数,这可能不是合适的构造函数,它还会将Any 转换为AnyRef,这很可能没问题,但如果你有AnyVal,它就会中断。我理解你在这里想要做什么,我认为 shapeless 可能是镜头的更好用法(不是我有一个很好的例子给你)或者只是直接合并你的案例类。
  • 感谢 cmets。 AnyRef 与 AnyVal 是我没有考虑过的最令人担忧的问题。在更高的层次上,鉴于整个过程和相关问题所涉及的限制,我可能最好不要再试图强行回到案例课程。我的最终目标功能要求不依赖于它,天真让它看起来像是一个好主意。我会研究 Shapeless,即使我最终没有使用它,支持文档似乎有助于我进一步理解 Scala 中的类型和泛型。
  • 作为参考,我发现这些文章阐明了我最初声明的目标中涉及的泛型多态性类型的潜在问题。特别是第二部分似乎以一些相关的观察结束part 1part 2
  • 重新审视AnyAnyRef 的演员阵容不使用“AnyVal”(我假设您指的是map(_.asInstanceOf[AnyRef])),它真的会崩溃吗?您的示例在 Id 案例类(Int 和 Double)中有两个 AnyVal,我可以确认它确实像您演示的那样工作。我误解了你的评论吗?
猜你喜欢
  • 2021-11-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-25
  • 2011-01-14
  • 1970-01-01
相关资源
最近更新 更多