使用Java(+龙目岛)的不可变类在spark中进行反序列化错误

问题描述 投票:0回答:1

我有这个简单的模型课

@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构造函数且没有设置方法。我应该怎么做?

java apache-spark deserialization immutability
1个回答
1
投票

只需给它提供无参数的无参数构造函数。这将是可变的,但比您提供所有二传手的方式要少一些混乱。您的all-args构造函数仍然可以使用null和荒谬的值来调用。如果要对对象的有效性强加某些约定,请显式使用验证。如果您追求的是不变性,那么您的成员将不再是最终的

反序列化通过调用最简单的(no-args)构造函数来进行,因为对于使用的实用程序的作者来说,这是一个更容易实现的过程,而不是将一个对all-args构造函数的调用与所有必要的属性顺序组合在一起。是任意的,并且不能保证分配给对象属性。

相反,它们创建vanila对象,并通过setter或反射将其填充,以确保名称在序列化版本和对象版本之间匹配。 All-args构造函数执行此操作的可靠性较差,并且难以实现。

您可以在Spark中选择反序列化(您是否正在使用Kryo btw?)有一个自定义反序列化器的概念,它可以避免使用no-args构造函数,但这意味着为所有类编写一些自定义的近乎样板。

© www.soinside.com 2019 - 2024. All rights reserved.