我正在使用Apache Beam和Kotlin构建一个简单的ETL管道,我正在尝试创建一种Either
类型:
@DefaultCoder(SerializableCoder::class)
sealed class Result<out T> : Serializable {
class Valid<out T>(val value: T) : Result<T>()
class Invalid(val reason: String): Result<Nothing>()
}
例如,这将使用:
abstract class MapFunction<T, U> : DoFn<Result<T>, Result<U>>() {
abstract fun map(item: T) : Result<U>
@ProcessElement
fun processElement(context: ProcessContext) {
val input = context.element()
val output = when (input) {
is Result.Valid -> map(input.value)
is Result.Invalid -> input
}
context.output(output)
}
}
这样我就可以跟踪所有无效结果并在管道末尾输出它们。问题是,当我构建一个基本的管道时,我会遇到以下神秘错误(为便于阅读而格式化):
Exception in thread "main" java.lang.IllegalArgumentException:
nl.sanderp.beam.functions.LyricsEnricher,
@ProcessElement processElement(ProcessContext),
@ProcessElement processElement(ProcessContext),
parameter of type DoFn<Result<Song>, Result<Song>>.ProcessContext at index 0:
ProcessContext argument must have type DoFn<Result<? extends Song>, Result<? extends Song>>.ProcessContext
我尝试使用in the Kotlin docs提到的@JvmWildcard
注释,但这只会导致更多错误:
Exception in thread "main" java.lang.ClassCastException: org.apache.beam.sdk.repackaged.com.google.common.reflect.Types$WildcardTypeImpl cannot be cast to java.lang.reflect.TypeVariable
如果可能的话,我似乎无法弄清楚如何让这个代码段工作。
供参考,以下是如何调用上述代码:
class LyricsEnricher(private val resourceFactory: ResourceFactory) : MapFunction<Song, Song>() {
private lateinit var dao: LyricsDao
@Setup
fun setup() {
dao = resourceFactory.get(ResourceFactory.Key.LYRICS_DAO)
}
override fun map(item: Song): Result<Song> {
dao.findLyrics(title = item.title, artist = item.artist)?.also {
return Result.Valid(item.copy(lyrics = it))
}
return Result.Invalid("No lyrics found")
}
}
答案 0 :(得分:1)
我认为与out
这样使用时,Invalid类的类型不正确。
您的光束贴图功能属于MapFunction<T, Result<U>>
类型,但您不能在任何地方指定U
,但在密封类内部,它会成为(双关语)T
或{ {1}}。
如果您将Nothing
的类型更改为Invalid<Nothing>
,那么将地图的类型设置为Invalid<T>
光束似乎就可以了。