Kotlin成员和扩展同时

时间:2017-10-04 09:59:33

标签: kotlin kotlin-extension

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

然而,即使在搜索了一段时间后,我也未能找到适合以下问题的答案:

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

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

现在我怎样才能从其他地方引用这个函数,以后再调用它上面的invoke()?

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

目前给我:

  

错误:(57,55)类型不匹配:推断类型是KFunction2但是(Int) - >布尔是   expectedError:(57,41)'isViewVisibility'是成员和扩展名   同时。不允许引用此类元素

有没有解决方法? 谢谢!

6 个答案:

答案 0 :(得分:3)

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

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

答案 1 :(得分:1)

错误消息指出:

'isViewVisibility'是成员和扩展名。不允许引用此类元素

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

// 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

答案 2 :(得分:0)

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

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

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

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

答案 3 :(得分:0)

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

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

但实际上扩展类型与正常的函数类型大多是可互换的(赋值兼容),接收者是第一个参数:

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

答案 4 :(得分: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)
}

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

答案 5 :(得分:0)

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

我找到了一种解决方法,方法是传递具有与扩展名相同签名的功能,然后将该功能委托给实际的扩展功能。

MyUtils.kt:

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

    }
}

AnyClass.kt:

//importing extension from MyUtils
import MyUtils.extend

fun someMethodWithLambda(func: (Int)->Unit) {}

class AnyClass {
    fun someMethod() {
      //error 
      someMethodWithLambda(MyClass::extend) //member and extension at the same time

      //workaround
      val myClassInstance = MyClass()
      someMethodWithLambda { val ->
          myClassInstance.extend(val)
      }

    }
}