Home > Software engineering >  Why is this Kotlin class property not shadowed by a method parameter?
Why is this Kotlin class property not shadowed by a method parameter?

Time:12-28

Looking at this code in kotlinx.coroutines, I noticed something strange:

/**
 * Returns a flow containing the results of applying the given [transform] function to each value of the original flow.
 */
public inline fun <T, R> Flow<T>.map(crossinline transform: suspend (value: T) -> R): Flow<R> = transform { value ->
    return@transform emit(transform(value))
}

In the first line, the transform used is clearly this.transform (defined here). Shouldn't the transform declared in the method parameter have been used instead, as it is in the second line?


To test this, I wrote a small class which tries to mimc this behaviour:

// flow.kt

class Flow(val name: String) {
    public fun transform (transform: (Any) -> Unit): Flow {
        return Flow("transformed")
    }

    public fun emit(value: Any) {
        // do nothing
    }

    public fun map(transform: (Any) -> Unit): Flow = transform { value ->
         return@transform(emit(transform(value)))
    }
}

And I get the kind of warning I was expecting when I run kotlinc flow.kt:

flow.kt:12:54: error: type mismatch: inferred type is Unit but Flow was expected
    public fun map(transform: (Any) -> Unit): Flow = transform { value ->
                                                     ^
flow.kt:12:66: error: cannot infer a type for this parameter. Please specify it explicitly.
    public fun map(transform: (Any) -> Unit): Flow = transform { value ->
                                                                 ^

(Kotlin version as returned by kotlinc -version is "kotlinc-jvm 1.6.10 (JRE 17.0.1 1)")

So why is it that the code defined in kotlinx.coroutines works? If I understand Kotlin's name shadowing rules correctly it shouldn't have.

CodePudding user response:

In kotlinx.couroutines, the transform parameter takes an argument of type T. Hence, this.transform is used when transform is called with a lambda argument.

In your example, the transform parameter takes an argument of type Any. A lambda is an Any and hence the parameter is being used instead of this.transform. Replacing Any with a type parameter will make your code compile too.

  • Related