Kotlin 中的静态扩展方法
Static extension methods in Kotlin
如何在 Kotlin 中定义静态扩展方法?这可能吗?我目前有如下所示的扩展方法。
public fun Uber.doMagic(context: Context) {
// ...
}
可以在实例上调用上述扩展。
uberInstance.doMagic(context) // Instance method
但是我如何使它成为如下所示的静态方法。
Uber.doMagic(context) // Static or class method
您可以使用 Companion 对象创建静态方法,例如:
class Foo {
// ...
companion object {
public fun bar() {
// do anything
}
}
}
然后你可以这样称呼它:
class Baz {
// ...
private fun callBar() {
Foo.bar()
}
}
要实现Uber.doMagic(context)
,可以对Uber
的companion object写一个扩展(需要伴随对象声明):
class Uber {
companion object {}
}
fun Uber.Companion.doMagic(context: Context) { }
我也非常喜欢在 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) }
官方文档是这样说的:
Kotlin generates static methods for package-level functions. Kotlin
can also generate static methods for functions defined in named
objects or companion objects if you annotate those functions as
@JvmStatic. For example:
class C {
companion object {
@JvmStatic fun foo() {}
fun bar() {}
}
}
Now, foo() is static in Java, while bar() is not:
C.foo(); // works fine
C.bar(); // error: not a static method
推荐你看看thislink。如您所见,您只需要在包(文件)的 top-level 处声明方法:
package strings
public fun joinToString(...): String { ... }
这等于
package strings;
public class JoinKt {
public static String joinToString(...) { ... }
}
constans 都是一样的。这个声明
val UNIX_LINE_SEPARATOR = "\n"
等于
public static final String UNIX_LINE_SEPARATOR = "\n";
实际上我在 30 分钟前就有了这个确切的问题,所以我开始四处寻找并找不到任何解决方案或解决方法,但是在搜索时我在 Kotlinglang 网站上发现 this section 指出:
Note that extensions can be defined with a nullable receiver type. Such extensions can be called on an object variable even if its value is null.
然后我有了一个有史以来最疯狂的想法,为什么不定义一个带有可空接收器的扩展函数(而不实际使用该接收器),然后在空对象上调用它!
所以我试了一下,效果很好,但看起来很丑。是这样的:
(null as Type?).staticFunction(param1, param2)
所以我通过在我的接收器类型的扩展文件中创建一个值为 null 的 val 然后在我的另一个 class 中使用它来解决这个问题。
因此,作为示例,下面是我如何为 Android 中的 Navigation
class 实现 "static" 扩展函数:
在我的 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)
显然,这不是一个 class 名称,它只是一个具有空值的 class 类型的变量。这在扩展制造商方面(因为他们必须创建变量)和开发人员方面(因为他们必须使用 SType
格式而不是实际的 class 名称)显然是丑陋的,但它与实际静态函数相比,是目前可以实现的最接近的。希望 Kotlin 语言的开发者能够响应所创建的 issue 并在语言中添加该功能。
要在 kotlin 中创建扩展方法,您必须创建一个 kotlin 文件(不是 class),然后在该文件中声明您的方法
例如:
public fun String.toLowercase(){
// **this** is the string object
}
在您正在处理的 class 或文件中导入函数并使用它。
由于我在搜索时不断遇到这个问题,这里有一个不同的方法,我还没有看到任何人提到它以静态方式工作并且它适用于泛型!
扩展定义:
// 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
,因为它可以被静态引用。
我还需要使用静态方法扩展 Java 对象的能力,发现对我来说最好的解决方案是创建一个扩展 Java class 和在那里添加我的方法。
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)
如何在 Kotlin 中定义静态扩展方法?这可能吗?我目前有如下所示的扩展方法。
public fun Uber.doMagic(context: Context) {
// ...
}
可以在实例上调用上述扩展。
uberInstance.doMagic(context) // Instance method
但是我如何使它成为如下所示的静态方法。
Uber.doMagic(context) // Static or class method
您可以使用 Companion 对象创建静态方法,例如:
class Foo {
// ...
companion object {
public fun bar() {
// do anything
}
}
}
然后你可以这样称呼它:
class Baz {
// ...
private fun callBar() {
Foo.bar()
}
}
要实现Uber.doMagic(context)
,可以对Uber
的companion object写一个扩展(需要伴随对象声明):
class Uber {
companion object {}
}
fun Uber.Companion.doMagic(context: Context) { }
我也非常喜欢在 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) }
官方文档是这样说的:
Kotlin generates static methods for package-level functions. Kotlin can also generate static methods for functions defined in named objects or companion objects if you annotate those functions as @JvmStatic. For example:
class C {
companion object {
@JvmStatic fun foo() {}
fun bar() {}
}
}
Now, foo() is static in Java, while bar() is not:
C.foo(); // works fine
C.bar(); // error: not a static method
推荐你看看thislink。如您所见,您只需要在包(文件)的 top-level 处声明方法:
package strings
public fun joinToString(...): String { ... }
这等于
package strings;
public class JoinKt {
public static String joinToString(...) { ... }
}
constans 都是一样的。这个声明
val UNIX_LINE_SEPARATOR = "\n"
等于
public static final String UNIX_LINE_SEPARATOR = "\n";
实际上我在 30 分钟前就有了这个确切的问题,所以我开始四处寻找并找不到任何解决方案或解决方法,但是在搜索时我在 Kotlinglang 网站上发现 this section 指出:
Note that extensions can be defined with a nullable receiver type. Such extensions can be called on an object variable even if its value is null.
然后我有了一个有史以来最疯狂的想法,为什么不定义一个带有可空接收器的扩展函数(而不实际使用该接收器),然后在空对象上调用它! 所以我试了一下,效果很好,但看起来很丑。是这样的:
(null as Type?).staticFunction(param1, param2)
所以我通过在我的接收器类型的扩展文件中创建一个值为 null 的 val 然后在我的另一个 class 中使用它来解决这个问题。
因此,作为示例,下面是我如何为 Android 中的 Navigation
class 实现 "static" 扩展函数:
在我的 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)
显然,这不是一个 class 名称,它只是一个具有空值的 class 类型的变量。这在扩展制造商方面(因为他们必须创建变量)和开发人员方面(因为他们必须使用 SType
格式而不是实际的 class 名称)显然是丑陋的,但它与实际静态函数相比,是目前可以实现的最接近的。希望 Kotlin 语言的开发者能够响应所创建的 issue 并在语言中添加该功能。
要在 kotlin 中创建扩展方法,您必须创建一个 kotlin 文件(不是 class),然后在该文件中声明您的方法 例如:
public fun String.toLowercase(){
// **this** is the string object
}
在您正在处理的 class 或文件中导入函数并使用它。
由于我在搜索时不断遇到这个问题,这里有一个不同的方法,我还没有看到任何人提到它以静态方式工作并且它适用于泛型!
扩展定义:
// 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
,因为它可以被静态引用。
我还需要使用静态方法扩展 Java 对象的能力,发现对我来说最好的解决方案是创建一个扩展 Java class 和在那里添加我的方法。
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)