scala: java.lang.reflect.Field的可序列化替代方案



假设User是一个保存用户信息的case类:

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。

使用productElement应该没有序列化问题,因为字段被数字替换了。但是我知道产品要素的顺序不能保证。

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

或者你可以创建一个类,它仍然可以是一个函数(这里更通用一点;如果不需要,更改为仅使用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已经已知,这就是为什么它必须被序列化):

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

相关内容

  • 没有找到相关文章

最新更新