Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kotlin member and extension at the same time

In an attempt to understand more about Kotlin and play around with it, I'm developing a sample Android app where I can try different things.

However, even after searching on the topic for a while, I haven't been able to find a proper answer for the following issue :

Let's declare a (dummy) extension function on View class :

fun View.isViewVisibility(v: Int): Boolean = visibility == v

Now how can I reference this function from somewhere else to later call invoke() on it?

val f: (Int) -> Boolean = View::isViewVisibility

Currently gives me :

Error:(57, 35) Type mismatch: inferred type is KFunction2 but (Int) -> Boolean was expectedError:(57, 41) 'isViewVisibility' is a member and an extension at the same time. References to such elements are not allowed

Is there any workaround? Thanks !

like image 238
NSimon Avatar asked Oct 04 '17 09:10

NSimon


4 Answers

Extensions are resolved statically, where the first parameter accepts an instance of the receiver type. isViewVisibility actually accept two parameters, View and Int. So, the correct type of it should be (View, Int) -> Boolean, like this:

val f: (View, Int) -> Boolean = View::isViewVisibility
like image 150
BakaWaii Avatar answered Nov 01 '22 06:11

BakaWaii


The error message states:

'isViewVisibility' is a member and an extension at the same time. References to such elements are not allowed

It's saying that the method is both an extension function, which is what you're wanting it to be, and a member. You don't show the entire context of your definition, but it probably looks something like this:

// MyClass.kt

class MyClass {
  fun String.coolStringExtension() = "Cool $this"

  val bar = String::coolStringExtension
}

fun main() {
    print(MyClass().bar("foo"))
}

Kotlin Playground

As you can see the coolStringExtension is defined as a member of MyClass. This is what the error is referring to. Kotlin doesn't allow you to refer to extension function that is also a member, hence the error.

You can resolve this by defining the extension function at the top level, rather than as a member. For example:

// MyClass.kt

class MyClass {
  val bar = String::coolStringExtension
}

fun String.coolStringExtension() = "Cool $this"

fun main() {
    print(MyClass().bar("foo"))
}

Kotlin Playground

like image 42
Jonah H. Avatar answered Nov 01 '22 05:11

Jonah H.


A better fit is the extension function type View.(Int) -> Boolean:

val f: View.(Int) -> Boolean = View::isViewVisibility

But actually the extension types are mostly interchangeable (assignment-compatible) with normal function types with the receiver being the first parameter:

View.(Int) -> Boolean(View, Int) -> Boolean

like image 23
hotkey Avatar answered Nov 01 '22 06:11

hotkey


I faced the same problem when I declared extension function inside another class and try to pass that extension function as parameter.

I found a workaround by passing function with same signature as extension which in turn delegates to actual extension function.

MyUtils.kt:

object MyUtils {
    //extension to MyClass, signature: (Int)->Unit
    fun MyClass.extend(val:Int) {
        
    }
}

AnyClass.kt:

//importing extension from MyUtils
import MyUtils.extend

// Assume you want to pass your extension function as parameter
fun someMethodWithLambda(func: (Int)->Unit) {}

class AnyClass {
    fun someMethod() {
      //this line throws error
      someMethodWithLambda(MyClass::extend) //member and extension at the same time
  
      //workaround
      val myClassInstance = MyClass()
      // you pass a proxy lambda which will call your extension function
      someMethodWithLambda { someIntegerValue ->
          myClassInstance.extend(someIntegerValue)
      }

    }
}
like image 21
Jegan Babu Avatar answered Nov 01 '22 04:11

Jegan Babu