[英]How to serialize functions in Scala?
I'm cutting my teeth on akka-persistence and came upon the quintessential problem of object serialization. 我正在削弱akka-persistence,并发现了对象序列化的典型问题。 My objects (shown below) have basic types, and functions.
我的对象(如下所示)具有基本类型和功能。 I read this , this and this , but none has helped me in making the following serializable.
我读了这个 , 这个和这个 ,但没有一个帮我制作下面的序列化。
Test Util 测试工具
object SerializationUtil {
def write(obj: Any): String = {
val temp = Files.createTempFile(null, null).toFile
val out = new ObjectOutputStream(new FileOutputStream(temp))
out.writeObject(obj)
out.close()
temp.deleteOnExit()
temp.getAbsolutePath
}
def read[T](file: String) = {
val in = new ObjectInputStream(new FileInputStream(new File(file)))
val obj = in.readObject().asInstanceOf[T]
in.close()
obj
}
}
Stats 统计
case class Stats(
app: String,
unit: ChronoUnit,
private var _startupDurations: List[Long]
) {
def startupDurations = _startupDurations.sorted
def startupDurations_=(durations: List[Long]) = _startupDurations = durations
@transient lazy val summary: LongSummaryStatistics = {
_startupDurations.asJava.stream()
.collect(summarizingLong(identity[Long]))
}
}
Stats
serializes just fine. Stats
序列化很好。
"SerializationUtil" should "(de)serialize Stats" in {
val file = SerializationUtil.write(newStats())
val state = SerializationUtil.read[Stats](file)
verifyStats(state)
}
But this doesn't: case class GetStatsForOneRequest(app: String, callback: Stats => Unit)
但这不是:
case class GetStatsForOneRequest(app: String, callback: Stats => Unit)
java.io.NotSerializableException: org.scalatest.Assertions$AssertionsHelper
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
Also tried: 还尝试过:
trait SerializableRunnable[T] extends scala.Serializable with ((T) => Unit)
implementing the callback as an instance of SerializableRunnable
, but no luck. 将回调实现为
SerializableRunnable
一个实例,但没有运气。
Ideas? 想法?
Edit : 编辑 :
Perhaps I should clarify the actual use case that is running into this issue to provide more context. 也许我应该澄清在此问题中遇到的实际用例以提供更多上下文。 The function is a callback from Akka HTTP route like the following:
该函数是来自Akka HTTP 路由的回调,如下所示:
path("stats") {
logRequest("/stats") {
completeWith(instanceOf[List[Stats]]) { callback =>
requestHandler ! GetStatsRequest(callback)
}
}
}
The handler actor persists the request until it gets a response. 处理程序 actor会持久保存请求,直到获得响应。 It may take more than one response to construct the final output.
构造最终输出可能需要不止一个响应。
I did some digging and it appears that the callback implementation is a CallbackRunnable . 我做了一些挖掘,看起来回调实现是一个CallbackRunnable 。
Maybe you didn't fully understand the linked articles. 也许你没有完全理解链接的文章。 The problem with function serialization is that anything captured in the closure must also be serializable.
函数序列化的问题是闭包中捕获的任何东西也必须是可序列化的。 What you need is Spores .
你需要的是孢子 。 Everything is explained there, but here is gist:
一切都在那里解释,但这里有要点:
Lambda functions in Scala can refer to variables in the outer scope without explicitly listing them as parameters. Scala中的Lambda函数可以引用外部作用域中的变量,而无需将它们明确地列为参数。 A function that does this is called a closure and the outer variables it refers to are captured .
执行此操作的函数称为闭包,并且捕获它引用的外部变量。 For example
foo
is captured in closure passed to map
below: 例如
foo
是在传递给下面的map
闭包中捕获的:
val foo = 42
List(1,2,3).map(_ + foo)
Looking at the example above where foo
is a primitive value, you woudn't think that's a problem. 看看上面
foo
是原始值的例子,你不认为这是一个问题。 But what happens when there is an enclosing class? 但是当有一个封闭的课程时会发生什么?
class C {
val myDBconn = ...
val foo = 42
List(1,2,3).map(_ + foo)
}
Now (unexpectedly for many programmers) the closure captures the entire this
of the non-serializable enclosing class, including myDBconn
, because foo
refers to the getter method this.foo
. 现在(意外许多程序员)关闭捕获整个
this
非序列化封装类,包括myDBconn
,因为foo
指getter方法this.foo
。
The solution is to not capture this
in the closure. 解决方案是不要在封闭中捕获
this
。 For example creating a local val
for any value we need to capture makes the function serializable again: 例如创建本地
val
因为我们需要捕获任何值使得再次函数序列化:
class C {
val myDBconn = ...
val foo = 42
{
val localFoo = foo
List(1,2,3).map(_ + localFoo)
}
}
Of course, doing this manually is tedious, hence Spores . 当然,手动执行此操作非常繁琐,因此Spores 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.