有时候,我想对泛型类型使用扩展函数,该泛型类型的参数或返回值是给定类型参数的子类型,但不必指定该实际类型参数。 (也许,如果不清楚,代码会更好地说明它?)
使用this answer的签名:
inline fun <reified U : T, T> Iterable<T>.partitionByType(): Pair<List<U>, List<T>>
它迫使我按如下方式使用它:
val list : List<SomeType>
list.partitionByType<InterestedType, SomeType>()
但是,我想写的是以下内容:
list.partitionByType<InterestedType>()
其中InterestedType
是列表的类型参数的子类型,即SomeType
。然后,以上两种解决方案都应该给我Pair<List<InterestedType>, List<SomeType>>
作为返回值。
但这实际上是不可能的。我想到的最好的签名如下:
inline fun <reified U : Any> Iterable<Any>.partitionByType(): Pair<List<U>, List<Any>>
但是随后我们丢失了实际已知的类型T
/ SomeType
。仍然可以在调用方进行未经检查的转换,但这不是我要的。
所以我的问题是:这可能吗?是否在未指定U : T
的地方放置了T
之类的东西?还是在这方面有什么计划(可以使用KEEP或Kotlin问题)?
对我来说,这听起来像是一项合理的功能,至少只要它仅适用于扩展功能即可。如果不是,那我现在可能会忽略/忽略的问题是什么? 我并不是真正想要的是一种解决方法(例如具有中间类型),但是也可以将其作为答案。
我想到的越多。如果将泛型类型的扩展函数声明如下,会更正确吗?
fun SomeType<T>.extended()
代替
fun <T> SomeType<T>.extended()
因为:如果我受SomeType<T>
的控制并在其中添加该函数,则不需要任何通用类型信息来声明它,例如那么下面的内容就足够了:
fun extended()
与具有自己的通用类型的函数相同,例如:
fun <U : T> extended2()
将其添加为扩展功能现在可以强制添加T
通用类型,尽管我们希望对其应用的类型显然是SomeType<T>
:
fun <T, U: T> SomeType<T>.extended2()
答案 0 :(得分:1)
注意:这仅是“中间类型”解决方案部分的自我解答。我仍然在寻找答案,如果没有这种解决方法可以解决该问题,或者有计划支持它。
用于解决该问题的中间类型示例如下:
class PartitionType<T>
inline fun <reified T> type() = PartitionType<T>()
inline fun <reified U : T, T> Iterable<T>.partitionBy(type: PartitionType<U> = type()): Pair<List<U>, List<T>> {
val first = ArrayList<U>()
val second = ArrayList<T>()
for (element in this) {
if (element is U) first.add(element)
else second.add(element)
}
return Pair(first, second)
}
这种方式可以写:
list.partitionBy(type<InterestedType>())
这将导致返回Pair<List<InterestedType>, List<SomeType>>
返回值,但需要添加该类型无论如何都不会使用(间接除外;在这里inline class
可能有意义,但是...应该该参数是吗?)。
这里的关键是仅将感兴趣的类型作为参数传递,而不是将其指定为请求的泛型类型(partitionBy<InterestedType, SomeType>()
仍然可以使用(或更正确地说:仍然需要))。
也许我用错了字,这就是为什么我找不到合适的东西的原因。也许这只是我想要拥有的东西,没有其他人吗?至少对我来说,听起来好像可以/应该起作用。
另一种变体是在已知类型上显式指定所有扩展功能,但随后我们需要注意平台冲突,例如:
@JvmName("partitionSomeTypeByType") // only needed if we require more partitionByType-functions with differing generic type parameters
inline fun <reified U : SomeType> Iterable<SomeType>.partitionByType() = partitionBy<U, SomeType>()
但同样只有一种解决方法。
答案 1 :(得分:1)
另一个不太优雅的解决方案是返回一个包装器对象(同样是没有意义的),因为它不是通用的,并且每个函数都需要样板,因此比通过type<T>()
更糟糕。
但是,通过使用inline
类,可以在没有任何运行时开销的情况下实现它,因此无论如何我都将其张贴在这里:
fun main() {
println(listOf<Number>(1, 2.0, 3f).doPartition.byType<Int>())
}
inline class PartitionByTypeWrapper<T>(val list: List<T>) {
inline fun <reified U : T> byType(): Pair<List<U>, List<T>> {
val (us, ts) = list.partition { it is U }
@Suppress("UNCHECKED_CAST")
return us as List<U> to ts
}
}
inline val <T> List<T>.doPartition get() = PartitionByTypeWrapper(this)