在 Scala 中,如何以编程方式确定案例类的字段名称?
在 Scala 中,假设我有一个像这样的案例类:
case class Sample(myInt: Int, myString: String)
有没有办法让我获得 Seq[(String, Class[_])]
,或者更好的是,Seq[( String, Manifest)]
,描述案例类的参数?
In Scala, suppose I have a case class like this:
case class Sample(myInt: Int, myString: String)
Is there a way for me to obtain a Seq[(String, Class[_])]
, or better yet, Seq[(String, Manifest)]
, describing the case class's parameters?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
我正在回答我自己的问题以提供基本解决方案,但我也在寻找替代方案和改进。
一种选择是使用 ParaNamer,它也与 Java 兼容并且不限于案例类。在 Scala 中,另一个选项是解析附加到生成的类文件的
ScalaSig
字节。这两种解决方案都不适用于 REPL。这是我尝试从 ScalaSig 中提取字段名称(使用 scalap 和 Scala 2.8.1):
免责声明:我不太了解 ScalaSig 的结构,应该考虑这一点作为一种启发式方法。 特别是,此代码做出以下假设:
的MethodEntry
,其所有者的 ID 为 0。在嵌套案例类上它将失败(因为没有 ScalaSig)。
此方法也仅返回
Class
实例,而不返回Manifest
。请随时提出改进建议!
I'm answering my own question to provide a base solution, but I'm looking for alternatives and improvements, too.
One option, also compatible with Java and not restricted to case classes, is to use ParaNamer. In Scala, another option is to parse the
ScalaSig
bytes attached to generated classfiles. Both solutions won't work in the REPL.Here's my attempt at extracting the names of the fields from
ScalaSig
(which uses scalap and Scala 2.8.1):Disclaimer: I don't really understand the structure of ScalaSig and this should be considered as a heuristics. In particular, this code makes the following assumptions:
ClassSymbol
.MethodEntry
with name<init>
whose owner has id 0.It will fail (because of no
ScalaSig
) on nested case classes.This method also only returns
Class
instances and notManifest
s.Please feel free to suggest improvements!
这是使用纯 Java 反射的不同解决方案。
要获取
Seq[(String, Class[_])]
,您可以执行以下操作:我不确定如何获取
Manifests
。Here's a different solution that uses plain-Java reflection.
To get a
Seq[(String, Class[_])]
, you can do this:I'm not sure about how to get
Manifests
.又是我(两年后)。这是使用 Scala 反射的不同解决方案。它的灵感来自 博客文章,其本身的灵感来自于Stack Overflow 交换。下面的解决方案专门针对上述原始发帖者的问题。
在一个编译单元(REPL
:paste
或已编译的 JAR)中,包含scala-reflect
作为依赖项并编译以下内容(在 Scala 2.11 中测试,可能 在 Scala 2.10 中工作):在另一个编译单元中(REPL 中的下一行或使用前一行作为依赖项编译的代码),像这样使用它:
这似乎有点矫枉过正,但我还没有能够要得到再短一点。它的作用如下:
caseClassFields
函数创建一个中间CaseClassFieldsExtractor
,它隐式地存在、报告其发现并消失。CaseClassFieldsExtractor
是一个具有伴随对象的特征,该伴随对象使用宏定义该特征的匿名具体子类。该宏可以检查案例类的字段,因为它具有有关案例类的丰富的编译器级信息。CaseClassFieldsExtractor
及其伴随对象必须在检查案例类的前一个编译单元中声明,以便宏在您想要使用它时存在。WeakTypeTag
传入。这评估为具有大量模式匹配的 Scala 结构,但我找不到任何文档。CaseClassFieldsExtractor
的匿名具体子类。caseClassFields
) 中,而不会在尚未定义时过早调用。欢迎任何可以完善此解决方案或解释“隐式”如何准确执行其操作(或是否可以将其删除)的评论。
It's me again (two years later). Here's a different, different solution using Scala reflection. It is inspired by a blog post, which was itself inspired by a Stack Overflow exchange. The solution below is specialized to the original poster's question above.
In one compilation unit (a REPL
:paste
or a compiled JAR), includescala-reflect
as a dependency and compile the following (tested in Scala 2.11, might work in Scala 2.10):And in another compilation unit (the next line in the REPL or code compiled with the previous as a dependency), use it like this:
It seems like overkill, but I haven't been able to get it any shorter. Here's what it does:
caseClassFields
function creates an intermediateCaseClassFieldsExtractor
that implicitly comes into existence, reports its findings, and disappears.CaseClassFieldsExtractor
is a trait with a companion object that defines an anonymous concrete subclass of this trait, using a macro. It is the macro that can inspect your case class's fields because it has rich, compiler-level information about the case class.CaseClassFieldsExtractor
and its companion object must be declared in a previous compilation unit to the one that examines your case class so that the macro exists at the time you want to use it.WeakTypeTag
. This evaluates to a Scala structure with lots of pattern matching and no documentation that I could find.CaseClassFieldsExtractor
.caseClassFields
) without being called too early, when it's not yet defined.Any comments that could refine this solution or explain how exactly the "implicits" do what they do (or if they can be removed) are welcome.
除了 Jim Pivarski 的回答之外,为了获得正确的字段顺序,需要在
decls
之后添加sorted
addition to Jim Pivarski answer, to have right order of fields, need to add
sorted
afterdecls