关于Scala案例类的思考

Mat*_*t R 12 reflection scala case-class scala-2.8

我正在尝试编写一个特性(在Scala 2.8中),它可以混合到一个case类中,允许在运行时检查它的字段,以用于特定的调试目的.我想按照它们在源文件中声明的顺序返回它们,我想省略case类中的任何其他字段.例如:

trait CaseClassReflector extends Product {

  def getFields: List[(String, Any)] = {
    var fieldValueToName: Map[Any, String] = Map()
    for (field <- getClass.getDeclaredFields) {
      field.setAccessible(true)
      fieldValueToName += (field.get(this) -> field.getName) 
    }
    productIterator.toList map { value => fieldValueToName(value) -> value }
  }

}

case class Colour(red: Int, green: Int, blue: Int) extends CaseClassReflector {
  val other: Int = 42
}

scala> val c = Colour(234, 123, 23)
c: Colour = Colour(234,123,23)

scala> val fields = c.getFields    
fields: List[(String, Any)] = List((red,234), (green,123), (blue,23))
Run Code Online (Sandbox Code Playgroud)

上面的实现显然是有缺陷的,因为它通过这些字段上的值的相等性来猜测字段在Product中的位置与其名称之间的关系,因此以下,例如,将不起作用:

Colour(0, 0, 0).getFields
Run Code Online (Sandbox Code Playgroud)

有什么方法可以实现吗?

psp*_*psp 10

看看后备箱你会发现这个.听取评论,这是不支持的:但因为我也需要这些名字......

/** private[scala] so nobody gets the idea this is a supported interface.
 */
private[scala] def caseParamNames(path: String): Option[List[String]] = {
  val (outer, inner) = (path indexOf '$') match {
    case -1   => (path, "")
    case x    => (path take x, path drop (x + 1))
  }

  for {
    clazz <- getSystemLoader.tryToLoadClass[AnyRef](outer)
    ssig <- ScalaSigParser.parse(clazz)
  }
  yield {
    val f: PartialFunction[Symbol, List[String]] =
      if (inner.isEmpty) {
        case x: MethodSymbol if x.isCaseAccessor && (x.name endsWith " ") => List(x.name dropRight 1)
      }
      else {
        case x: ClassSymbol if x.name == inner  =>
          val xs = x.children filter (child => child.isCaseAccessor && (child.name endsWith " "))
          xs.toList map (_.name dropRight 1)
      }

    (ssig.symbols partialMap f).flatten toList
  }
}
Run Code Online (Sandbox Code Playgroud)


Sag*_*ich 9

这是一个简短的工作版本,基于上面的例子

  trait CaseClassReflector extends Product {
    def getFields = getClass.getDeclaredFields.map(field => {
      field setAccessible true
      field.getName -> field.get(this)
    })
  }
Run Code Online (Sandbox Code Playgroud)


Rex*_*err 7

在每个例子中,我看到字段的顺序是相反的:getFields数组中的最后一项是case类中列出的第一项.如果您"很好地"使用案例类,那么您应该能够映射productElement(n)getDeclaredFields()( getDeclaredFields.length-n-1).

但这是相当危险的,因为我不知道规范中有什么东西坚持它必须是这样的,如果你在case类中覆盖val,它甚至不会出现在getDeclaredFields中(它会出现在那个超类的领域).

您可能会更改代码以假设事情是这样的,但检查具有该名称的getter方法和productIterator是否返回相同的值并抛出异常(如果它们没有)(这意味着您实际上并不知道对应的内容)什么).

  • @Core_Dumped - 实际上,我认为除了你可以使用我上面的模糊提示来制定你自己的解决方案之外,你更有可能遇到麻烦而不是提出问题的解决方案.正如我所说,"这是相当危险的".能够预见并避免问题是你的责任,这可能需要至少在这方面从"相对noob"过渡到"not not".在REPL中使用反射和示例案例类,看看是否可以搞清楚! (2认同)