仅将标记为具体化的类型传递给 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 个通用类型。 F
和 J
都需要给函数。虽然这对您(以及任何查看该方法的人)来说是显而易见的,但我认为在删除泛型类型时在运行时可能并不那么明显(但现在主要是猜测)。
如果您不介意您传递的水果与您期望的果汁亚型不匹配,那么以下可能适合您:
inline fun <reified J : Juice<out Fruit>> makeJuice(fruit : Fruit) : J = TODO()
但是如果你想确保AppleJuice
只能用Apple
构造,那么我只能想到类似下面的解决方案:
将 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>
.
但也许 none 这些就是您希望得到的。
所以如果你想用一个单一的方法来处理这一切,第三个变体可能是你的最佳选择(即使它使用包装器)。
假设我有以下代码:
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 个通用类型。 F
和 J
都需要给函数。虽然这对您(以及任何查看该方法的人)来说是显而易见的,但我认为在删除泛型类型时在运行时可能并不那么明显(但现在主要是猜测)。
如果您不介意您传递的水果与您期望的果汁亚型不匹配,那么以下可能适合您:
inline fun <reified J : Juice<out Fruit>> makeJuice(fruit : Fruit) : J = TODO()
但是如果你想确保AppleJuice
只能用Apple
构造,那么我只能想到类似下面的解决方案:
将
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>
.
但也许 none 这些就是您希望得到的。 所以如果你想用一个单一的方法来处理这一切,第三个变体可能是你的最佳选择(即使它使用包装器)。