Java 束流传输的非法突变异常

Java 束流传输的非法突变异常,java,google-cloud-dataflow,apache-beam,Java,Google Cloud Dataflow,Apache Beam,这是我写的Apache Beam PTransform: public class NormalizeTransform extends PTransform<PCollection<String>, PCollection<SimpleTable>> { @Override public PCollection<SimpleTable> expand(PCollection<String> lines) { ExtractF

这是我写的Apache Beam PTransform:

public class NormalizeTransform
  extends PTransform<PCollection<String>, PCollection<SimpleTable>> {

@Override
public PCollection<SimpleTable> expand(PCollection<String> lines) {
  ExtractFields extract_i = new ExtractFields();
  PCollection<SimpleTable> table = lines
    .apply("Extracting data model fields from lines",
           ParDo.of(extract_i));
}                                                   

public class ExtractFields extends DoFn<String, SimpleTable> {

@ProcessElement
public void processElement(ProcessContext c){
  try {
    String line = c.element();              
    // fill table
    for (Table_Struct st: this.struct){
      String o = line.substring(st.pos_1, st.pos_2));
      this.table.getClass().getField(st.Field_Name).set(
        this.table, o);                                                                     
    }
    c.output(this.table);
  }
}

我不认为我在代码中的任何地方专门更改了值的任何输出。突变检测器将比较两个值:previousValue和newValue。在我的例子中,previousValue通常是一个输入值,newValue是另一个输入值。为什么转换会尝试使用一个输入值来修改另一个输入值?

我不确定这个表是从哪里来的

但是为了帮助您理解错误消息,请记住,
processElement
可以在多个输入上调用。第一次调用将输出
this.table
。下一次调用将在输出此表之前对其进行变异

如果此变异发生在第一次调用输出了
this.table
之后,并且下游代码还没有机会读取
this.table
之前,那么您将得到不正确的结果。因此,此错误表示您在输出引用后对
this.table
的内容进行了变异,这是您不应该做的

请考虑(1)输出此.table的副本,或者(2)将该表创建为本地字段。例如:

@ProcessElement
public void processElement(ProcessContext c){
  try {
    String line = c.element(); 
    Table table = /* create the table */;             
    // fill table
    for (Table_Struct st: this.struct){
      String o = line.substring(st.pos_1, st.pos_2));
      this.table.getClass().getField(st.Field_Name)
        .set(table, o);                                                                     
    }
    c.output(table);
  }
}

还要注意,在每个
processElement
中执行反射可能比期望的慢。如果可以直接修改字段,则可能会更好。

还要注意,强制比较反序列化的值,因此它假定反序列化是确定性的。如果对象包含集合,则需要注意元素的顺序。

非常感谢您的回复。这就解释了原因。实际上,我确实担心
processElement
中的反射可能会拖累性能。但在这种情况下,表是运行时定义的,不能直接修改,因此我求助于使用反射。你有比反射更好的主意吗?因为它是在运行时定义的,所以除了反射之外,你没有其他的选择。这取决于您可以使用哪些其他选项/库来避免反射。您对其他选项/库有何建议?感谢@Ben Chambers,其他选项包括使用AutoValue、Avro或协议缓冲区从模式生成代码,以及使用它们。很大程度上取决于您的实际用例是什么。您可能需要更详细地描述您正在尝试做什么,并提出一个更一般的Java问题。
@ProcessElement
public void processElement(ProcessContext c){
  try {
    String line = c.element(); 
    Table table = /* create the table */;             
    // fill table
    for (Table_Struct st: this.struct){
      String o = line.substring(st.pos_1, st.pos_2));
      this.table.getClass().getField(st.Field_Name)
        .set(table, o);                                                                     
    }
    c.output(table);
  }
}