Передача только типов, отмеченных как reified, в общую функцию Kotlin
Допустим, у меня есть следующий код:
open class Fruit
class Apple : Fruit()
open class Juice<T : Fruit>
class AppleJuice : Juice<Apple>()
fun <F : Fruit, J : Juice<F>> makeJuice(juiceClass : Class<J>, fruit : F) : J {}
Я называю функцию так:
val appleJuice : AppleJuice = makeJuice(AppleJuice::class.java, Apple())
Но вместо передачи объекта класса я хотел бы передать AppleJuice
как тип:
val appleJuice : AppleJuice = makeJuice<AppleJuice>(Apple())
Я изменил свою функцию, чтобы включить reified
:
inline fun <F : Fruit, reified J : Juice<F>> makeJuice(fruit : F) : J {}
Но теперь я должен указать оба типа:
val appleJuice : AppleJuice = makeJuice<Apple, AppleJuice>(Apple())
Теоретически, Apple
тип не должен быть нужен, потому что он уже известен из AppleJuice
тип. Можно ли как-то избавиться от передачи ненужных типов и пропустить только те, которые помечены как reified
?
1 ответ
Основная проблема, которую я вижу с вашим решением, заключается в том, что вы запрашиваете 2 общих типа на вашем makeJuice
-метод. И то и другое F
а также J
должны быть переданы функции. Хотя это очевидно для вас (и любого, кто смотрит на метод), я думаю, что это может быть не столь очевидно во время выполнения, когда стираются универсальные типы (но сейчас это в основном предположение).
Если вы не возражаете, что фрукты, которые вы раздаете, на самом деле не соответствуют подтипам сока, который вы ожидаете, то вам может пригодиться следующее:
inline fun <reified J : Juice<out Fruit>> makeJuice(fruit : Fruit) : J = TODO()
Однако, если вы хотите убедиться, что AppleJuice
может быть построен только с Apple
s, тогда я могу думать только о решениях, подобных следующему:
добавив
makeJuice
кFruit
классы, напримерabstract class Fruit { abstract fun makeJuice() : Juice<out Fruit> } // and subclasses: class Apple : Fruit() { override fun makeJuice(): AppleJuice = TODO() }
добавив
makeJuice
(/makeFrom
?) кJuice
классы, напримерopen class Juice<T : Fruit> { fun makeFrom(fruit : T) { TODO() } }
добавление любого другого промежуточного объекта, чтобы вам не требовалось 2 общих типа, например,
class JuiceMaker<F : Fruit>(val fruit : F) { inline fun <reified J : Juice<F>> makeJuice() : J = TODO() } fun <F : Fruit> using(fruit : F) = JuiceMaker(fruit)
и позвонив с
using(Apple()).makeJuice<AppleJuice>()
варианты вышеупомянутых с использованием функций расширения, например
inline fun <reified J : Juice<out Apple>> Apple.makeJuice() : J = TODO()
но вам нужно указать его для всех типов. Следующие, к сожалению, не будут работать:
inline fun <F : Fruit, reified J : Juice<F>> F.makeJuice() : J = TODO()
как тогда у нас опять такая же проблема... и нужно указать
<Apple, AppleJuice>
,
Но, возможно, ничего из этого вы не надеялись получить. Поэтому, если вы хотите иметь один метод, который обрабатывает все это, третий вариант, вероятно, ваш лучший выбор (даже если он использует обертку).