同时为 Kotlin 成员和扩展

Kotlin member and extension at the same time

为了更多地了解 Kotlin 并尝试使用它,我正在开发一个示例 Android 应用程序,我可以在其中尝试不同的东西。

然而,即使在这个主题上搜索了一段时间后,我也未能找到以下问题的正确答案:

让我们在视图上声明一个(虚拟的)扩展函数 class :

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

现在我如何从其他地方引用这个函数以便稍后调用它的 invoke()?

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

目前给我:

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

有什么解决办法吗? 谢谢!

作为解决方法,您可以创建一个单独的 普通 函数并从内联扩展方法中调用它:

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

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

您不能直接调用扩展方法,因为您没有可用的隐式 this 对象。

扩展是静态解析的,其中第一个参数接受接收者类型的实例。 isViewVisibility实际上接受两个参数,ViewInt。所以,它的正确类型应该是 (View, Int) -> Boolean,像这样:

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

更合适的是扩展函数类型 View.(Int) -> Boolean:

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

但实际上扩展类型大部分是可互换的(赋值兼容)与普通函数类型以接收者为第一个参数:

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

使用带有两个参数的类型(第一个用于隐式接收者,正如@Bakawaii 已经提到的)或扩展类型都应该在没有任何警告的情况下工作。

我们以这个函数为例:

fun String.foo(f: Int) = true

您可以将其分配给具有两个参数函数类型的 属性,如下所示:

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

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

或者,您可以使用扩展函数类型,然后您可以使用以下两种语法之一调用它:

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

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

同样,以上内容应该 运行 没有任何错误或警告。

当我在另一个 class 中声明扩展函数并尝试将该扩展函数作为参数传递时,我遇到了同样的问题。

我通过传递与扩展具有相同签名的函数找到了一个变通方法,该函数又委托给实际的扩展函数。

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

    }
}

错误消息指出:

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

它是说该方法既是一个扩展函数,这正是您想要的,也是一个成员。您没有显示定义的整个上下文,但它可能看起来像这样:

// MyClass.kt

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

  val bar = String::coolStringExtension
}

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

Kotlin Playground

如您所见,coolStringExtension 被定义为 MyClass 的成员。这就是错误所指的内容。 Kotlin 不允许您引用也是成员的扩展函数,因此会出现错误。

您可以通过在顶层而不是成员定义扩展函数来解决这个问题。例如:

// MyClass.kt

class MyClass {
  val bar = String::coolStringExtension
}

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

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

Kotlin Playground