Kotlin member and extension at the same time
Asked Answered
P

6

10

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 !

Platitudinous answered 4/10, 2017 at 9:59 Comment(0)
P
8

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
Philipps answered 4/10, 2017 at 10:38 Comment(4)
You're correct about the type, it solved the TypeMismatch. I'm still left with the "member and extension at the same time", which seems not supported for now. I'll accept your answer, we'll see if the language will support this at some point. Thanks !Platitudinous
Yes, it seems the proposal is still under consideration as metioned in this answer.Philipps
@Platitudinous Btw, I've just omitted this error that you mentioned. Sorry about that, I just think that your extension function is an top-level function. You may provide a more concrete example to demonstrate this problem, e.g. write a complete class with the extension function declared inside the class, to see if someone can give you extra explanation on this issue beyond this answer.Philipps
Sorry, I know this is old, but for the sake of people searching solutions: In fact the problem is, that your function is NOT a top-level function. Extensions defined as class members of other classes cannot be referenced. Dunno why, though. Just define your extension as a top-level-function and you should be fine.Bucktooth
G
6

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

Gabbro answered 3/1, 2020 at 22:11 Comment(0)
M
1

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

Manaker answered 4/10, 2017 at 11:18 Comment(0)
U
1

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)
      }

    }
}
Unstring answered 24/7, 2019 at 10:8 Comment(0)
C
0

As a workaround you can create a separate normal function and invoke it from an inline extension method:

inline fun View.isVisibility(v: Int): Boolean = isViewVisibility(this, v)

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

You can't call directly the extension method because you don't have the implicit this object available.

Cram answered 4/10, 2017 at 10:16 Comment(1)
The problem I faced wasn't the implementation of <pre>View.isVisibility<code> but how to reference it. Therefore perhaps the only way is the other way round (a normal function, that we can reference, and that would invoke the extension function on View)Platitudinous
M
0

Using either a type with two parameters (the first for the implicit receiver, as @Bakawaii has already mentioned) or an extension type should both work without any warnings at all.

Let's take this function as an example:

fun String.foo(f: Int) = true

You can use assign this to a property that has a two parameter function type like this:

val prop: (String, Int) -> Boolean = String::foo

fun bar() {
    prop("bar", 123)
}

Or, you can use an extension function type, that you can then call with either of these two syntaxes:

val prop2: String.(Int) -> Boolean = String::foo

fun bar2() {
    prop2("bar2", 123)
    "bar2".prop2(123)
}

Again, the above should all run without any errors or warnings.

Moke answered 4/10, 2017 at 11:20 Comment(5)
Hmm, interesting. What Kotlin version do you have? I should definitely work on 1.1.50.Moke
Update: trying this in the online IDE at try.kotlinlang.org doesn't work with 1.1.4 selected, only with 1.1.50 and later. Perhaps this was fixed in that version then.Moke
You're right, this is odd. I'm running version 1.1.51 in AndroidStudio. It works fine on the link you provided, but not in AS. Maybe updates didn't go all the way to the plugin yet?Platitudinous
Is your Kotlin plugin also updated to 1.1.51? It works for me in AS as well.Moke
I'm on AS 3.0 Beta 7 with (topLevel gradle file having classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version") and (module level gradle file having implementation"org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"). Kotlin version is defined in topLevel gradle file as ext.kotlin_version = '1.1.51'. So I should be on the latest yes.Platitudinous

© 2022 - 2024 — McMap. All rights reserved.