仅将标记为具体化的类型传递给 Kotlin 泛型函数

Passing only types marked as reified to Kotlin generic function

假设我有以下代码:

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

但是我不想传递 class 对象,而是想传递 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 方法中要求 2 个通用类型。 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>.

但也许 none 这些就是您希望得到的。 所以如果你想用一个单一的方法来处理这一切,第三个变体可能是你的最佳选择(即使它使用包装器)。