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

scala: serializable alternatives to java.lang.reflect.Field

假设 User 是一个案例 class,其中包含有关用户的信息:

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

给定一个字段名(例如 "name""age"),我想 return 一个 函数 来提取这个字段(没有再次解析字段名)。简而言之,我需要这个来工作:

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 不可序列化。

有什么建议或替代方案吗?

备注/更多背景:

Using productElement should work without serialization issues since fields are replaced with numbers. But I understand there are no guarantees on the order of the product elements.

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

或者你可以制作一个 class,它仍然可以是一个函数(这里更通用一些;如果不需要,更改为仅使用 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 在您创建函数对象时已经知道并且 "captured" 通过它,这就是它必须被序列化的原因):

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