Передача только типов, отмеченных как 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 может быть построен только с Apples, тогда я могу думать только о решениях, подобных следующему:

  1. добавив makeJuice к Fruit классы, например

    abstract class Fruit {
      abstract fun makeJuice() : Juice<out Fruit>
    }
    // and subclasses:
    class Apple : Fruit() {
      override fun makeJuice(): AppleJuice = TODO()
    }
    
  2. добавив makeJuice (/makeFrom?) к Juice классы, например

    open class Juice<T : Fruit> {
      fun makeFrom(fruit : T) { TODO() }
    }
    
  3. добавление любого другого промежуточного объекта, чтобы вам не требовалось 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>()
    
  4. варианты вышеупомянутых с использованием функций расширения, например

    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>,

Но, возможно, ничего из этого вы не надеялись получить. Поэтому, если вы хотите иметь один метод, который обрабатывает все это, третий вариант, вероятно, ваш лучший выбор (даже если он использует обертку).

Другие вопросы по тегам