I want to generify the following function:
fun ViewGroup.allRadioButtons(f: (RadioButton) -> Unit){
this.afterMeasured {
for(i in 0 until childCount){
val child = getChildAt(i)
if(child is RadioButton){
f(child)
}
if(child is ViewGroup){
child.allRadioButtons(f)
}
}
}
}
So instead of hardcoding RadioButton
, I would like to use a generic T
, like this:
inline fun <reified T> ViewGroup.allViewsOfTypeT(f: (T) -> Unit){
this.afterMeasured {
for(i in 0 until childCount){
val child = getChildAt(i)
if(child is T){
f(child)
}
if(child is ViewGroup){
child.allRadioButtons(f)
}
}
}
}
I can't do the above because reified types are not allowed in recursive functions.
How can I generify that function in Kotlin?
You can make the recursive function non-inline and take a KClass
representing the desired type, and make an additional wrapper function:
fun <T : View> ViewGroup.allViewsOfTypeT(type: KClass<T>, f: (T) -> Unit) {
afterMeasured {
for (i in 0 until childCount) {
val child = getChildAt(i)
if (type.isInstance(child)) f(child)
if (child is ViewGroup) child.allViewsOfTypeT(type, f)
}
}
}
inline fun <reified T : View> ViewGroup.allViewsOfTypeT(f: (T) -> Unit)
= allViewsOfTypeT(T::class, f)
You can't inline a recursive function, unless you can unroll it into a loop, because inlining a function means that after compilation it isn't a function anymore - it is instead copied directly into the call site. No function, no call stack, no recursion. In these cases, you have to pass a KClass
instead of making the generic parameter reified, which is basically exactly what you'd do in Java if you needed an instanceof
check with a generic parameter.
However, you can roll your own stack (Way to go from recursion to iteration):
inline fun <reified T : View> ViewGroup.allViewsOfTypeT(action: (T) -> Unit) {
val views = Stack<View>()
afterMeasured {
views.addAll((0 until childCount).map(this::getChildAt))
}
while (!views.isEmpty()) {
views.pop().let {
if (it is T) action(it)
if (it is ViewGroup) {
afterMeasured {
views.addAll((0 until childCount).map(this::getChildAt))
}
}
}
}
}
I haven't tested this, but the general idea should work.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With