У меня есть заводский интерфейс в моем коде 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>>
и возвращает ResultMapper<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) } }