scala - 在 Scala 中,如何以编程方式确定案例类的字段名称?

标签 scala reflection case-class

在 Scala 中,假设我有一个这样的案例类:

case class Sample(myInt: Int, myString: String)

有没有办法让我获得 Seq[(String, Class[_])] ,或者更好,Seq[(String, Manifest)] ,描述案例类的参数?

最佳答案

我正在回答我自己的问题以提供基本解决方案,但我也在寻找替代方案和改进。

一种也与 Java 兼容且不限于 case 类的选项是使用 ParaNamer .在 Scala 中,另一种选择是解析 ScalaSig附加到生成的类文件的字节。这两种解决方案都不适用于 REPL。

这是我从 ScalaSig 中提取字段名称的尝试(使用 scalap 和 Scala 2.8.1):

def valNames[C: ClassManifest]: Seq[(String, Class[_])] = {
  val cls = classManifest[C].erasure
  val ctors = cls.getConstructors

  assert(ctors.size == 1, "Class " + cls.getName + " should have only one constructor")
  val sig = ScalaSigParser.parse(cls).getOrElse(error("No ScalaSig for class " + cls.getName + ", make sure it is a top-level case class"))

  val classSymbol = sig.parseEntry(0).asInstanceOf[ClassSymbol]
  assert(classSymbol.isCase, "Class " + cls.getName + " is not a case class")

  val tableSize = sig.table.size
  val ctorIndex = (1 until tableSize).find { i =>
    sig.parseEntry(i) match {
      case m @ MethodSymbol(SymbolInfo("<init>", owner, _, _, _, _), _) => owner match {
        case sym: SymbolInfoSymbol if sym.index == 0 => true
        case _ => false
      }
      case _ => false
    }
  }.getOrElse(error("Cannot find constructor entry in ScalaSig for class " + cls.getName))

  val paramsListBuilder = List.newBuilder[String]
  for (i <- (ctorIndex + 1) until tableSize) {
    sig.parseEntry(i) match {
      case MethodSymbol(SymbolInfo(name, owner, _, _, _, _), _) => owner match {
        case sym: SymbolInfoSymbol if sym.index == ctorIndex => paramsListBuilder += name
        case _ =>
      }
      case _ =>
    }
  }

  paramsListBuilder.result zip ctors(0).getParameterTypes
}

免责声明:我不太了解 ScalaSig 的结构,这应该被视为一种启发式方法。 特别是,此代码做出以下假设:
  • 案例类只有一个构造函数。
  • 位置零处的签名条目始终是 ClassSymbol .
  • 该类的相关构造函数是第一个MethodEntry与姓名 <init>其所有者的 ID 为 0。
  • 参数名称具有构造函数条目的所有者,并且始终在该条目之后。

  • 它会在嵌套的 case 类上失败(因为没有 ScalaSig)。

    此方法也仅返回 Class实例而不是 Manifest s。

    请随时提出改进建议!

    关于scala - 在 Scala 中,如何以编程方式确定案例类的字段名称?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6282464/

    相关文章:

    go - 如何通过 Go 中的反射更新结构数组字段值?

    c# - 如何在C#中写一个 "truly"私有(private)方法?

    scala - 将工作分配给多个核心 : Hadoop or Scala's parallel collections?

    scala - 没有代码更改的 Scala Maven 重新编译错误

    android - 使用 IntelliJ 12 制作 Android+Scala 项目的简单方法?

    scala - 自制提取器和案例类提取器的区别

    scala - 默认案例类参数取决于其他参数 scala

    Scala 运算符的奇怪之处

    c# - 反射 .NET Core 创建 Lambda 表达式

    斯卡拉玩 2.0 : Is it possible to use case classes in "routes"