Why is this reified-T related method failing to compile?

Consider the following attempt to produce a forEach() method that recursively traverses the given View hierarchy and consume-s all View(s) of the specified type T:

    private inline fun <reified T: View> forEach(view: View, noinline consume:  (T) -> Unit) {
        forEachEx(T::class.java, view, consume)
    }

    private fun <T: View> forEachEx(c: Class<T>, view: View, consume:  (T) -> Unit) {
        if (c.isInstance(view))
            consume(view as T)

        if (view is ViewGroup) {
            view.children.forEach { childView ->
                forEachEx(c, childView, consume)
            }
        }
    }

The call to forEachEx(T::class.java, view, consume) fails to compile with this somehow cryptic message:

None of the following substitutions
(Class<CapturedTypeConstructor(out Int)>,View,(CapturedTypeConstructor(out Int)) -> Unit)
(Class<View>,View,(View) -> Unit)
(Class<T#2 (type parameter of com.xyz.MainActivity.forEach)>,View,(T#2) -> Unit)
can be applied to
(Class<out Int>,View,(T#2) -> Unit)

Explain this to me, please.
And how to get a forEach function that would be nicely called like this:
forEach<TextView>(topView) { /* do something with each TextView */ }

Funny enough, your example compiles for me without problems.

If you want to do this without reflection (without the Class parameter), you would have to move the recursive part out of the function performing the type check - this example should work, although there are most likely better ways than unrolling the hierarchy into a sequence beforehand.

fun getAllViewsInHierarchy(view: View): Sequence<View> = sequence {
    yield(view)

    if (view is ViewGroup) yieldAll(view.children.flatMap { getAllViewsInHierarchy(it) })
}

private inline fun <reified T : View> forAllInHierarchy(view: View, noinline block: (T) -> Unit) {
    getAllViewsInHierarchy(view)
        .filterIsInstance<T>()
        .forEach(block)
}
2 Likes

That’s weird :slight_smile:
What is your dev env and target?
(Mine is AndroidStudio with JDK8 and Kotlin 1.3.61, (and if that matters: targeting API level 21 (Android 5.0))

Thanks for the advise with fun getAllViewsInHierarchy(view: View): Sequence<View>

1 Like