Kotlin 工厂接口与泛型

Kotlin factory interface with generics

我的 Kotlin 代码中有一个工厂接口,就像这样(使用 Guava 的 TypeToken):

interface ResultMapperFactory {
    fun <T> get(type: TypeToken<T>, context: MapperLookupContext): ResultMapper<T>?
}

到目前为止,还不错。这在调用时非常容易使用,但是实现几乎总是需要不安全的转换:

object StringFactory : ResultMapperFactory {
    override fun <T> get(type: TypeToken<T>, context: MapperLookupContext): ResultMapper<T>? {
        return if (type.rawType == String::class.java) MyStringMapper as ResultMapper<T> else null
    }
}

这很丑陋,但我用一个漂亮的技巧克服了它。首先,一个用于创建 TypeToken 个实例的小实用函数:

inline fun <reified T> typeToken(): TypeToken<T> = object : TypeToken<T>() {}

现在我向我的工厂接口添加了一个伴随对象:

companion object {

    inline operator fun <reified R> invoke(crossinline body: (TypeToken<R>, MapperLookupContext) -> ResultMapper<R>?): ResultMapperFactory {
        val t = typeToken<R>().type
        return object : ResultMapperFactory {
            override fun <T> get(type: TypeToken<T>, context: MapperLookupContext): ResultMapper<T>? {
                return if (type.isSubtypeOf(t)) body(type as TypeToken<R>, context) as ResultMapper<T>? else null
            }
        }
    }
}

这允许我在一个地方进行未经检查(但安全)的转换,我可以编写如下代码:

val stringMapper: ResultMapper<String> = TODO()
val stringMapperFactory = ResultMapperFactory<String> { type, context ->
    stringMapper
}

然而,一旦我想实现一个采用 TypeToken<List<T>> 和 returns 和 ResultMapper<List<T>> 的工厂,这种方法也会崩溃,因为我无处可放T参数。

我很想听听您的建议。

我自己找到了解决方案,使用我在原始问题中发布的 invoke 运算符 fun 我可以编写以下内容:

private class ListResultMapper<out E>(private val elementMapper: ResultMapper<E>) : ResultMapper<List<E>> { /* ... */ }

val listMapperFactory = ResultMapperFactory<List<*>> { type, context -> context.resultMapperFactory.get(type.elementType(), context)?.let { ListResultMapper(it) } }