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) } }
我的 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) } }