仅将标记为已归类的类型传递给Kotlin泛型函数

时间:2018-08-20 11:36:50

标签: kotlin kotlin-generics kotlin-reified-type-parameters

假设我有以下代码:

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 个答案:

答案 0 :(得分:5)

我看到的解决方案的主要问题是,您在makeJuice方法中要求两种通用类型。 FJ都需要赋予该功能。尽管对您(以及任何使用此方法的人)都很明显,但我认为在运行时擦除通用类型可能并不那么明显(但这在这里主要是猜测)。

如果您不介意所传递的水果与您期望的果汁的亚型不完全匹配,那么以下内容可能适合您:

inline fun <reified J : Juice<out Fruit>> makeJuice(fruit : Fruit) : J = TODO()

但是,如果您想确保AppleJuice只能用Apple来构造,那么我只能想到类似于以下的解决方案:

  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>

但也许这些都不是您希望得到的。 因此,如果您希望拥有一个可以处理所有问题的方法,那么第三个变体可能是您的最佳选择(即使它使用包装器也是如此)。