Kotlin工厂与泛型的界面
我在我的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>>
并返回一个TypeToken<List<T>>
的工厂,就会TypeToken<List<T>>
,因为我没有地方放这个T
参数。
我渴望听到你的建议。
我自己找到解决方案,使用invoke
操作符的乐趣,我张贴在我原来的问题,我可以写下面的内容:
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) } }