6

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

但是,即使在该主题上搜索了一段时间后,我也无法找到以下问题的正确答案:

让我们在 View 类上声明一个(虚拟)扩展函数:

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

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

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

目前给我:

错误:(57, 35) 类型不匹配:推断类型为 KFunction2 但 (Int) -> Boolean 是预期错误:(57, 41) 'isViewVisibility' 同时是成员和扩展。不允许引用此类元素

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

4

6 回答 6

8

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

val f: (View, Int) -> Boolean = View::isViewVisibility
于 2017-10-04T10:38:02.860 回答
2

错误消息指出:

'isViewVisibility' 同时是一个成员和一个扩展。不允许引用此类元素

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

// MyClass.kt

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

  val bar = String::coolStringExtension
}

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

科特林游乐场

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

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

// MyClass.kt

class MyClass {
  val bar = String::coolStringExtension
}

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

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

科特林游乐场

于 2020-01-03T22:11:12.747 回答
1

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

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

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

    }
}
于 2019-07-24T10:08:40.103 回答
1

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

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

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

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

于 2017-10-04T11:18:29.437 回答
0

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

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

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

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

于 2017-10-04T10:16:25.597 回答
0

使用带有两个参数的类型(第一个用于隐式接收器,正如@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)
}

同样,以上内容都应该在没有任何错误或警告的情况下运行。

于 2017-10-04T11:20:13.200 回答