Scala/Java类参数为何不支持序列化?以Beam为例
核心问题出在Scala编译器对构造器参数的引用处理,结合Beam的序列化规则导致:
直接使用构造器参数
stringP时的问题:
当你在DummyTransform中直接传入stringP(SomeClass的构造器参数),Scala编译器会默认将这个参数绑定为SomeClass的成员变量(因为被内部的DummyTransform引用)。此时DummyTransform会隐式持有一个外围SomeClass实例的引用。
Beam在序列化DoFn(这里是DummyTransform)时,会递归序列化它所有关联的对象。而SomeClass中包含了不可序列化的Pipeline对象(Pipeline包含运行时状态、资源句柄等无法序列化的内容),因此触发NonSerializableException。使用局部变量
serializedStringP后的变化:
把stringP赋值给代码块内的局部变量后,Scala编译器会做优化:让DummyTransform直接持有这个String对象的引用,不再关联外围的SomeClass实例。此时序列化DummyTransform时,只会序列化String本身(String是JDK内置的可序列化类型),不会牵扯到Pipeline或SomeClass,因此可以正常运行。额外验证点:
如果你的SomeClass没有持有Pipeline这类不可序列化的成员,直接使用构造器参数也不会出现序列化异常——问题的本质是构造器参数的引用导致外围类被连带序列化,而外围类存在不可序列化的对象。
附原始对比代码:
直接使用构造器参数(失败):
SomeClass(p: Pipeline, stringP: String){ val foo: PCollection[String] = p.apply("TestSerialization", ParDo.of(new DummyTransform(stringP)) }
改用局部变量(成功):
SomeClass(p: Pipeline, stringP: String){ val foo: PCollection[String] = { val serializedStringP = stringP p.apply("TestSerialization", ParDo.of(new DummyTransform(serializedStringP)) } }
内容的提问来源于stack exchange,提问作者Dasph

