【问题标题】:Passing only types marked as reified to Kotlin generic function仅将标记为 reified 的类型传递给 Kotlin 泛型函数
【发布时间】:2018-08-20 11:36:49
【问题描述】:

假设我有以下代码:

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 的类型?

【问题讨论】:

  • 在这种情况下是否可以将makeJuice 函数放在Juice 类中?
  • 如果您实际传入哪个Fruit 并不重要,那么您可以删除F 并改用Juice&lt;out Fruit&gt;Fruit 参数。
  • @Mosius 不,这是不可能的。必须是静态方法
  • 扩展函数也是静态方法...扩展函数可以吗? ;-)
  • 好吧...我不认为您可以按照您想要的方式拥有一个具有具体类型的静态函数(即您实际上需要 2 个泛型类型)。我的答案中的变体 3 可能是您在不编写大量额外实现的情况下可以获得的最好的。

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


【解决方案1】:

我在您的解决方案中看到的主要问题是,您在 makeJuice-method 上要求 2 个泛型类型。 FJ 都需要赋予该函数。虽然这对您(以及任何查看该方法的人)来说是显而易见的,但我认为在运行时擦除泛型类型时可能并不那么明显(但现在主要是这里的猜测)。

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

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()
    

    因为我们又遇到了同样的问题...并且需要指定&lt;Apple, AppleJuice&gt;

但也许这些都不是您希望得到的。 因此,如果您想要一个方法来处理这一切,那么第三种变体可能是您的最佳选择(即使它使用了包装器)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-10-13
    • 2011-02-20
    • 1970-01-01
    • 1970-01-01
    • 2019-03-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多