scala:java.lang.reflect.Field

时间:2015-09-11 00:12:38

标签: scala serialization reflection apache-spark case-class

假设User是一个包含用户信息的案例类:

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

给定一个字段名称(例如"name""age"),我想返回一个提取此字段的函数(不再解析字段名称)。简而言之,我需要这个工作:

val u = User(name = "john",age = 44)
val func = extractFunctionFromFieldName("name") // returns func: User => Any
func(u) // return "john"

阅读反思,我得到了类似的东西:

def extractFunctionFromFieldName(s: String): User => Any = {
  val f = classOf[User].getDeclaredField(s)
  f.setAccessible(true)
  u: User => f.get(u)
}

问题是此函数不可序列化,因为java.lang.reflect.Field不可序列化。

有任何建议或替代方案吗?

备注/更多背景:

  • 字段列表比{name,age}大得多,并且不断增长。这就是我想避免硬编码的原因
  • 使用productElement应该可以在没有序列化问题的情况下工作,因为字段会被数字替换。但据我所知,产品元素的顺序无法保证。
  • BTW,我需要它可序列化,因为我使用apache spark。

1 个答案:

答案 0 :(得分:2)

  

使用productElement应该没有序列化问题,因为字段被数字替换。但据我所知,产品元素的顺序无法保证。

有。对于案例类,它们将与参数的顺序相同。

或者你可以创建一个类,它仍然可以是一个函数(在这里更常见;如果不需要,更改为使用wow.min.js只应该很容易):

User

这将仅在第一次应用字段时初始化字段,因此可以在反序列化一次后多次应用,而无需重复case class ExtractField[T](s: String)(implicit t: ClassTag[T]) extends (T => Any) { @transient lazy val f = { val f = t.runtimeClass.getDeclaredField(s) f.setAccessible(true) f } def apply(x: T) = f.get(x) } 次调用。

我最初没有注意到问题已经提到这一点,所以我最初给出的另一个解决方案是不适用的:你可以在函数内移动getDeclaredField(在你的情况下f是当你创建函数对象并被它“捕获”时已经知道,这就是它必须被序列化的原因:

f