Kotlin - 如何生成无法具体化的递归函数?

Tia*_*ago 4 generics recursion kotlin

我想生成以下函数:

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)
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

因此RadioButton,我想使用 generic代替 hardcoding T,如下所示:

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)
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我无法执行上述操作,因为递归函数中不允许具体化类型。

如何在 Kotlin 中泛化该函数?

Moi*_*ira 6

您可以使递归函数成为非内联函数并采用 aKClass表示所需的类型,并创建一个额外的包装函数:

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)
Run Code Online (Sandbox Code Playgroud)

你不能内联一个递归函数,除非你可以将它展开到一个循环中,因为内联一个函数意味着在编译之后它不再是一个函数——而是直接复制到调用站点中。没有函数,没有调用栈,没有递归。在这些情况下,您必须传递 aKClass而不是使泛型参数具体化,如果您需要instanceof使用泛型参数进行检查,这基本上就是您在 Java 中所做的。

但是,您可以滚动自己的堆栈(从递归到迭代的方法):

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))
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我还没有测试过这个,但总体思路应该可行。