这与另一个SO问题[这里](Setting Custom Coders & Handling Parameterized types)有关。在解决方法之后,帮助我在变换中使用自定义类型。但由于我的自定义类型是通用的,我希望甚至使变换类通用,然后可以使用相同的类型参数化自定义类型。但是当我尝试这样做时,我遇到了无法为类型变量T提供编码器,因为实际类型因擦除而未知。工作中建议注册一个可以返回类型参数的编码器,但由于类型参数本身是未知的,我想这个异常被抛出,我不知道如何解决这个问题。
static class Processor<T>
extends PTransform<PCollection<String>,
PCollection<KV<String, Set<CustomType<T>>>>> {
private static final long serialVersionUID = 0;
@Override public PCollection<KV<String, Set<CustomType<T>>>>
apply(PCollection<String> items) {
PCollection<KV<String, Set<CustomType<T>>>> partitionedItems = items
.apply(ParDo.of(new ParDoFn()));
PCollection<KV<String, Set<CustomType<T>>>> combinedItems = partitionedItems
.apply(Combine.<String, Set<CustomType<T>>>perKey(new Merger()));
}
}
答案 0 :(得分:4)
这看起来也是由Github Issue #57引起的,应该与该问题一起解决。
与此同时,Dataflow实际上包含可以立即解决问题的高级功能。从您的代码段看来,整个系统可能看起来像这样:
class CustomType<T extends Serializable> { ... }
class Processor<T extends Serializable>
extends PTransform<PCollection<String>,
PCollection<KV<String, Set<CustomType<T>>>>> {
class ParDoFn extends DoFn<String, KV<String, Set<CustomType<T>>>> { … }
class Merger extends BinaryCombineFn<Set<CustomType<T>>> { … }
@Override
public PCollection<KV<String, Set<CustomType<T>>>>
apply(PCollection<String> items) {
PCollection<KV<String, Set<CustomType<T>>>> partitionedItems =
items.apply(ParDo.of(new ParDoFn()));
PCollection<KV<String, Set<CustomType<T>>>> combinedItems =
partitionedItems.apply(
Combine.<String, Set<CustomType<T>>, Set<CustomType<T>>>perKey(
new Merger()));
return combinedItems;
}
}
…
PCollection<String> input = ...
input.apply(new Processor<String>());
返回的DoFn
获取每个TypeDescriptor
的输出类型
因为ParDoFn
是Processor<T>
的内部类,所以输出类型描述符只是Set<CustomType<T>>
,即使它被实例化为新Processor<String>
。
要获取类型信息,我们需要ParDoFn
静态了解为T
提供的类型。这有两个步骤。
<强> 1。创建Processor
PCollection<String> input = ...
input.apply(new Processor<String>() {});
这确保了对于Processor
此实例的所有内部类,类型变量T
静态绑定到类型String
。在这种情况下,最好将Processor
作为一个抽象类,以便消费者将其子类化。
2.Override getOutputTypeDescriptor
ParDoFn
以解析外部类Processor
的类型。
class Processor<T extends Serializable> extends ... {
class ParDoFn extends DoFn<String, KV<String, Set<CustomType<T>>>> {
@Override
protected TypeDescriptor<KV<String, Set<CustomType<T>>>>
getOutputTypeDescriptor() {
return new TypeDescriptor<KV<String, Set<CustomType<T>>>>(
Processor.this.getClass()) {};
}
}
从一开始,代码的完整工作版本如下。请再次注意,解析Github Issue #57时无需执行此操作。
class CustomType<T extends Serializable> { ... }
abstract class Processor<T extends Serializable>
extends PTransform<PCollection<String>,
PCollection<KV<String, Set<CustomType<T>>>>> {
class ParDoFn extends DoFn<String, KV<String, Set<CustomType<T>>>> {
...
@Override
protected TypeDescriptor<KV<String, Set<CustomType<T>>>>
getOutputTypeDescriptor() {
return new TypeDescriptor<KV<String, Set<CustomType<T>>>>(
Processor.this.getClass()) {};
}
}
class Merger extends BinaryCombineFn<Set<CustomType<T>>> { ... }
@Override
public PCollection<KV<String, Set<CustomType<T>>>> apply(PCollection<String> items) {
PCollection<KV<String, Set<CustomType<T>>>> partitionedItems =
items.apply(ParDo.of(new ParDoFn()));
PCollection<KV<String, Set<CustomType<T>>>> combinedItems =
partitionedItems.apply(
Combine.<String, Set<CustomType<T>>, Set<CustomType<T>>>perKey(
new Merger()));
return combinedItems;
}
}
PCollection<String> input = …;
input.apply(new Processor<String>() {});
这不是唯一的解决方案 - 您也可以覆盖Processor.getDefaultOutputCoder
或在中间partitionedItems
集合上明确调用setCoder
- 但它似乎是最常用的