【问题标题】:Deserialization Error in spark using immutable class with java (+ lombok)使用带有 java (+ lombok) 的不可变类的 spark 中的反序列化错误
【发布时间】:2020-01-06 19:53:03
【问题描述】:

我有这个简单的模型类

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

【问题讨论】:

    标签: java apache-spark deserialization immutability


    【解决方案1】:

    轻松的出路

    只需给它没有任何设置器的无参数构造函数。它将是可变的,但与您提供所有设置器相比,它的混乱程度略低。当你使用 Kryo 作为你的反序列化器 (我认为你已经这样做了)你可以保持这个构造函数私有。

    全参数构造函数仍然可以用空值和无意义的值调用。如果您想对对象的有效性强加一些合同,请明确使用验证。如果您追求的是不变性,那么您的成员将不再是最终的使用无参数构造函数。


    对象创建的动态特性

    反序列化通过调用最简单的(无参数)构造函数来工作,因为对于使用的实用程序的作者来说,实现这一过程要容易得多,而不是组装一个对具有所有必要属性顺序的全参数构造函数的调用,其顺序可能是任意的并且不能保证对对象属性的分配。

    相反,他们创建 vanila 对象并通过 setter 或反射填充它,确保名称在序列化版本和对象版本之间匹配。全参数构造函数会不太可靠地做到这一点,并且更难实现。

    在 Kryo 中创建自定义对象

    如果您需要保持不变性,则必须使用自定义对象创建。请看Kryo's example for custom object creation

    Registration registration = kryo.register(SomeClass.class);
    registration.setInstantiator(new ObjectInstantiator<SomeClass>() {
      public SomeClass newInstance () {
        return new SomeClass("some constructor arguments", 1234);
      }
    });
    

    【讨论】:

    • 感谢您的回答,但它并没有真正让我满意......放置一个无参数 ctor 迫使我实际编写一个全参数(不使用 lombok @Value)。这么多的代码......现在我可以理解,在一般情况下,使用所有arg构造函数并不是一件容易的事,但它适用于scala中的case-class,所以应该有一种方法:\
    • 你可以使用lombok的@NoArgsConstructor。也有可能(请测试)您的无参数构造函数可以保持私有(我会测试它 - 它还取决于您使用什么:如在 Kryo 或 Java 中)。
    • 如果我使用 NoArgConstructor,那么我不能有最终字段,否则我必须提供默认值,但是 AllArgCtor 不起作用......我尝试了一些东西,但无法让它工作没有@Setter :(
    猜你喜欢
    • 2016-05-22
    • 2017-08-24
    • 2012-07-22
    • 2018-08-14
    • 2018-10-04
    • 2020-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多