我有这个简单的模型课
@Value // lombok - create standard all arg constructor and getters
public class ModelA implements Serializable {
private String word;
private double value;
}
此简单测试失败:
public class SparkSerializationTest {
private SparkSession spark = SparkSession.builder()
.master("local")
.appName("Test")
.getOrCreate();
@Test
public void testSerializationModelA() {
ModelA modelA1 = new ModelA("A1", 12.34);
ModelA modelA2 = new ModelA("A2", 56.78);
Dataset<ModelA> dataset = spark.createDataset(
Arrays.asList(modelA1, modelA2),
Encoders.bean(ModelA.class));
List<ModelA> yo = dataset.collectAsList(); // <== *** failure here ***
assertThat(yo).isEqualTo(Arrays.asList(modelA1, modelA2));
}
}
例外:
java.util.concurrent.ExecutionException: org.codehaus.commons.compiler.CompileException: File 'generated.java', Line 24, Column 67: failed to compile: org.codehaus.commons.compiler.CompileException: File 'generated.java', Line 24, Column 67: No applicable constructor/method found for zero actual parameters; candidates are: "com.xxx.yyy.ModelA(java.lang.String, double)"
似乎需要一个零arg构造函数。但是我希望我的模型是不可变的,因此具有完整的arg构造函数且没有设置方法。我应该怎么做?
只需给它提供无参数的无参数构造函数。这将是可变的,但比您提供所有二传手的方式要少一些混乱。您的all-args构造函数仍然可以使用null和荒谬的值来调用。如果要对对象的有效性强加某些约定,请显式使用验证。如果您追求的是不变性,那么您的成员将不再是最终的
反序列化通过调用最简单的(no-args)构造函数来进行,因为对于使用的实用程序的作者来说,这是一个更容易实现的过程,而不是将一个对all-args构造函数的调用与所有必要的属性顺序组合在一起。是任意的,并且不能保证分配给对象属性。
相反,它们创建vanila对象,并通过setter或反射将其填充,以确保名称在序列化版本和对象版本之间匹配。 All-args构造函数执行此操作的可靠性较差,并且难以实现。
您可以在Spark中选择反序列化(您是否正在使用Kryo btw?)有一个自定义反序列化器的概念,它可以避免使用no-args构造函数,但这意味着为所有类编写一些自定义的近乎样板。