如何在Scala中序列化函数?

我正在切断akka-persistence,并发现了对象序列化的典型问题。 我的对象(如下所示)具有基本类型和function。 我读了这个 , 这个和这个 ,但没有一个帮我制作下面的序列化。

测试工具

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 } } 

统计

 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序列化很好。

 "SerializationUtil" should "(de)serialize Stats" in { val file = SerializationUtil.write(newStats()) val state = SerializationUtil.read[Stats](file) verifyStats(state) } 

但这不是: case class GetStatsForOneRequest(app: String, callback: Stats => Unit)

 java.io.NotSerializableException: org.scalatest.Assertions$AssertionsHelper at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184) 

还尝试过:

 trait SerializableRunnable[T] extends scala.Serializable with ((T) => Unit) 

将回调实现为SerializableRunnable一个实例,但没有运气。

想法?

编辑

也许我应该澄清在此问题中遇到的实际用例以提供更多上下文。 该函数是来自Akka HTTP 路由的回调,如下所示:

 path("stats") { logRequest("/stats") { completeWith(instanceOf[List[Stats]]) { callback => requestHandler ! GetStatsRequest(callback) } } } 

处理程序 actor会持久保存请求,直到获得响应。 构造最终输出可能需要不止一个响应。

我做了一些挖掘,看起来回调实现是一个CallbackRunnable 。

也许你没有完全理解链接的文章。 函数序列化的问题是闭包中捕获的任何东西也必须是可序列化的。 你需要的是孢子 。 一切都在那里解释,但这里有要点:

什么是关闭?

Scala中的Lambda函数可以引用外部作用域中的变量,而无需将它们明确地列为参数。 执行此操作的函数称为闭包,并且捕获它引用的外部变量。 例如foo是在传递给下面的map闭包中捕获的:

 val foo = 42 List(1,2,3).map(_ + foo) 

为什么序列化存在问题?

看看上面foo是原始值的例子,你不认为这是一个问题。 但是当有一个封闭的课程时会发生什么?

 class C { val myDBconn = ... val foo = 42 List(1,2,3).map(_ + foo) } 

现在(对于许多程序员来说意外),闭包捕获了非序列化封闭类的全部内容,包括myDBconn ,因为foo引用了getter方法this.foo

解决方案是什么?

解决方案是不在封闭中捕获this 。 例如,为我们需要捕获的任何值创建局部值会使函数再次可序列化:

 class C { val myDBconn = ... val foo = 42 { val localFoo = foo List(1,2,3).map(_ + localFoo) } } 

当然,手动执行此操作非常繁琐,因此Spores 。

Interesting Posts