【问题标题】:Static extension methods in KotlinKotlin 中的静态扩展方法
【发布时间】:2015-03-28 09:52:47
【问题描述】:

如何在 Kotlin 中定义静态扩展方法?这甚至可能吗?我目前有一个扩展方法如下图。

public fun Uber.doMagic(context: Context) {
    // ...
}

可以在实例上调用上述扩展。

uberInstance.doMagic(context) // Instance method

但是我如何使它成为如下所示的静态方法。

Uber.doMagic(context)         // Static or class method

【问题讨论】:

  • “静态扩展方法”是什么意思?
  • 我可以在没有实例的情况下调用的方法,但仍然是类的扩展。 (常规 Java 静态方法)
  • 我认为这可能不是 Kotlin extensions 好问题的预期用途,我在尝试推断 C# 概念时想到了同样的事情。但是在实践中,用法非常相似。 Kotlin 扩展,虽然它们声称是静态调度的,但它们感觉就像是动态“附加”的,如果有这样的事情,或者迟到绑定到实例。如果我没记错的话......或者我完全错了:)
  • 目前你不能编写一个扩展方法来调用一个类名而不是类实例。事实上,扩展函数需要一个 receiver parameter 是一个实例,所以没有办法跳过这部分。另一方面,我们正在研究一种为类对象编写扩展的方法,这样您就可以在类名上调用它,但接收者具有类对象的类型
  • @AndreyBreslav 您能否更新我们是否允许静态扩展功能?我也想念它。

标签: static-methods kotlin


【解决方案1】:

您可以使用 Companion 对象创建静态方法,例如:

class Foo {
    // ...
    companion object {
        public fun bar() {
            // do anything
        }
    }
}

然后你可以这样称呼它:

class Baz {
    // ...
    private fun callBar() {
        Foo.bar()
    }
}

【讨论】:

  • 我相信这实际上不是静态的。伴随对象允许您使用类名进行调用,但仍使用类的实例。此外,问题是关于静态扩展函数,而不仅仅是静态函数。但是要拥有静态函数,您可以使用 platformStatic 注释。
  • platformStatic 在当前 Kotlin 中重命名为 JvmStatic
【解决方案2】:

要实现Uber.doMagic(context),可以对Ubercompanion object写一个扩展(需要声明伴生对象):

class Uber {
    companion object {}
}

fun Uber.Companion.doMagic(context: Context) { }

【讨论】:

  • 这很好,但如果它是一个 Java 类或没有同伴的类怎么办?
  • @Jire 对于这种情况,Kotlin 1.0 不支持
  • 我可以看到一个用默认值扩展 JVM 框架类的用例,例如String.DEFAULT 或 Int.DEFAULT,以防您的域逻辑需要(我的当前需要,与空数据类一起使用)。
  • 只有在 UberKotlin 类的情况下才有效。最好也能静态扩展 Java
  • 这仍然是不可能的,你可以在这里看到:youtrack.jetbrains.com/issue/KT-11968 这里有一个相关的 SO 线程:stackoverflow.com/questions/33911457/…
【解决方案3】:

我也很喜欢在 Kotlin 中添加静态扩展方法。作为目前的解决方法,我将扩展方法添加到多个类,而不是在所有类中使用一个静态扩展方法。

class Util    

fun Util.isDeviceOnline(context: Context): Boolean {
    val connMgr = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
    val networkInfo = connMgr.activeNetworkInfo
    return networkInfo != null && networkInfo.isConnected
}

fun Activity.isDeviceOnline(context: Context) = { Util().isDeviceOnline(context) }
fun OkHttpClient.isDeviceOnline(context: Context) = { Util().isDeviceOnline(context) }

【讨论】:

  • 最初的问题是关于如何使用 静态方法 扩展 javaclass。在您的情况下,这意味着能够在没有Activity 实例的情况下直接调用Activity.isDeviceOnline(...)
【解决方案4】:

官方文档是这么说的:

Kotlin 为包级函数生成静态方法。科特林 还可以为命名中定义的函数生成静态方法 对象或伴随对象,如果您将这些函数注释为 @JvmStatic。例如:

Kotlin static methods

class C {
  companion object {
    @JvmStatic fun foo() {}
    fun bar() {}
  }
}

现在,foo() 在 Java 中是静态的,而 bar() 不是:

C.foo(); // works fine
C.bar(); // error: not a static method

【讨论】:

    【解决方案5】:

    推荐你看this链接。如您所见,您只需在包(文件)的顶层声明方法:

    package strings
    public fun joinToString(...): String { ... }
    

    这等于

    package strings;
    
    public class JoinKt {
        public static String joinToString(...) { ... }
    }
    

    有了常量,一切都是一样的。本声明

    val UNIX_LINE_SEPARATOR = "\n"
    

    等于

    public static final String UNIX_LINE_SEPARATOR = "\n";
    

    【讨论】:

      【解决方案6】:

      我实际上在 30 分钟前就有了这个确切的问题,所以我开始四处寻找,但找不到任何解决方案或解决方法,但在搜索时,我在 Kotlinglang 网站上找到了this section,上面写着:

      请注意,可以使用可为空的接收器类型来定义扩展。可以在对象变量上调用此类扩展,即使它的值为 null。

      那么我有一个最疯狂的想法,为什么不使用可空接收器定义扩展函数(实际上不使用该接收器),然后在空对象上调用它! 所以我试了一下,效果很好,但看起来很丑。是这样的:

      (null as Type?).staticFunction(param1, param2)
      

      所以我通过在接收器类型的扩展文件中创建一个值为 null 的 val 来解决这个问题,然后在我的其他类中使用它。 所以,作为一个例子,下面是我如何在 Android 中为 Navigation 类实现一个“静态”扩展函数: 在我的 NavigationExtensions.kt 文件中:

      val SNavigation: Navigation? = null
      fun Navigation?.createNavigateOnClickListener(@IdRes resId: Int, args: Bundle? = null, navOptions: NavOptions? = null,
                                                      navigationExtras: Navigator.Extras? = null) : (View) -> Unit {
          //This is just implementation details, don't worry too much about them, just focus on the Navigation? part in the method declaration
          return { view: View -> view.navigate(resId, args, navOptions, navigationExtras) }
      }
      

      在使用它的代码中:

      SNavigation.createNavigateOnClickListener(R.id.action_gameWonFragment_to_gameFragment)
      

      显然,这不是一个类名,它只是一个具有空值的类类型的变量。这在扩展制造商方面(因为他们必须创建变量)和开发人员方面(因为他们必须使用SType 格式而不是实际的类名)显然很难看,但它是最接近的与实际的静态功能相比,现在实现了。希望 Kotlin 语言开发者能够回复创建的 issue 并在语言中添加该功能。

      【讨论】:

      • Hacky 这可能是。但也聪明酷。这是对这个问题最有见地的回答。 Kotlin 尽其所能伪造提供一流的扩展,所以 IMO,你使用最好的解决方案来处理它的虚无。不错。
      【解决方案7】:

      要在 kotlin 中创建扩展方法,您必须创建一个 kotlin 文件(不是类),然后在文件中声明您的方法 例如:

      public fun String.toLowercase(){
          // **this** is the string object
      }
      

      在你正在处理的类或文件中导入函数并使用它。

      【讨论】:

      • 标题甚至不是问题
      【解决方案8】:

      由于我在搜索时不断遇到这种情况,所以我没有看到有人提到以静态方式工作的不同方法并且它适用于泛型!

      扩展定义:

      // Extension function
      fun <T> KClass<T>.doSomething() = /* do something */
      
      // Extension Property
      val <T> KClass<T>.someVal get() = /* something */
      

      用法:

      MyType::class.doSomething()
      
      MyType::class.someVal
      

      如您所见,诀窍是将扩展函数附加到类型的 KClass 上,因为它可以被静态引用。

      【讨论】:

        【解决方案9】:

        我还需要使用静态方法扩展 Java 对象的能力,并发现对我来说最好的解决方案是创建一个扩展 Java 类的 Kotlin 对象并在其中添加我的方法。

        object Colour: Color(){
            fun parseColor(r: Int?, g: Int?, b: Int?) = parseColor(String.format("#%02x%02x%02x", r, g, b))
        }
        

        调用:

        val colour = Colour.parseColor(62, 0, 100)
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-07-03
          • 2011-05-12
          • 2010-10-26
          • 2011-05-31
          • 2012-11-22
          • 2011-01-01
          • 2012-01-29
          相关资源
          最近更新 更多