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
不可序列化。
有什么建议或替代方案吗?
备注/更多背景:
- 字段列表比
{name,age}
大得多,而且还在不断增加。这就是为什么我想避免硬编码
- 使用
productElement
应该没有序列化问题,因为字段被替换为数字。但据我了解,产品元素的顺序无法保证。
- 顺便说一句,我需要它是可序列化的,因为我将它与 apache spark 一起使用。
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)
}
假设 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
不可序列化。
有什么建议或替代方案吗?
备注/更多背景:
- 字段列表比
{name,age}
大得多,而且还在不断增加。这就是为什么我想避免硬编码 - 使用
productElement
应该没有序列化问题,因为字段被替换为数字。但据我了解,产品元素的顺序无法保证。 - 顺便说一句,我需要它是可序列化的,因为我将它与 apache spark 一起使用。
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)
}