使用Kotlin的Beam DoFn中的泛型和方差

时间:2018-03-05 20:45:45

标签: kotlin apache-beam

我正在使用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")
    }
}

1 个答案:

答案 0 :(得分:1)

我认为与out这样使用时,Invalid类的类型不正确。

您的光束贴图功能属于MapFunction<T, Result<U>>类型,但您不能在任何地方指定U,但在密封类内部,它会成为(双关语)T或{ {1}}。

如果您将Nothing的类型更改为Invalid<Nothing>,那么将地图的类型设置为Invalid<T>光束似乎就可以了。