Kotlin-扩展
Posted bug樱樱
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kotlin-扩展相关的知识,希望对你有一定的参考价值。
一、扩展函数
1、扩展函数
扩展函数,就是从类的外部扩展出来的一个函数,这个函数看起来就像是类的成员方法一样。比如扩展一个String
的方法。我们经常需要切割字符串然后获取第几个元素,我们扩展一个类似的工具类方法,如下:
fun String.getStringAfterCut(regular: String, index: Int): String?
split(regular).let list ->
return list.getOrNull(index)
复制代码
调用
"a.5.c".getStringAfterCut(".",1)
扩展函数中扩展的对象String
在函数中被成为函数的接收者。
2、扩展函数的原理
我们将扩展函数和调用的方法都反编译,会得到下面的结果:
//扩展函数反编译
//省略了几行不影响分析的代码
public final class StringKtKt
@Nullable
public static final String getStringAfterCut(@NotNull String $this$getStringAfterCut, @NotNull String regular, int index)
//...
List var3 = StringsKt.split$default((CharSequence)$this$getStringAfterCut, new String[]regular, false, 0, 6, (Object)null);
//...
return (String)CollectionsKt.getOrNull(var3, index);
//调用处的反编译
StringKtKt.getStringAfterCut("a.5.c", ".", 1);
可以看到扩展函数并没有成为String
类的成员方法,仅仅是生成了StringKtKt
类的静态方法,而扩展的对象被当做参数传入进了这个静态方法中。
二、扩展属性
1、扩展属性
扩展属性,是在类的外部为它定义一个新的成员属性。例如下面的方法,获取字符串的第一个字符:
val String.firstChar: Char?
get() = if (isEmpty())
null
else
get(0)
调用
"a.5.c".firstChar
2、扩展属性的原理
反编译上面的代码看看结果:
//扩展属性反编译代码
//省略了几行不影响分析的代码
public final class StringKtKt
@Nullable
public static final Character getFirstChar(@NotNull String $this$firstChar)
//...
return var1.length() == 0 ? null : $this$firstChar.charAt(0);
//调用处的反编译
StringKtKt.getFirstChar("a.5.c");
可以看到和扩展函数类似,扩展属性并没有成为String
类的成员属性,仅仅是生成了StringKtKt
类的静态方法,而扩展的对象被当做参数传入进了这个静态方法中。
三、扩展函数和扩展属性应该如何选择
有些扩展可以写成扩展函数也可以写成扩展属性,那么应该如何选择? 如果语义上适合作为属性那么写成扩展属性比较合适,比如firstChar
,语义上适合作为扩展函数则写成扩展函数。
四、扩展的访问域
扩展的访问域只讨论扩展函数,扩展属性类似。
顶层扩展
:扩展的访问域仅限于该Kotlin文件当中的所有成员
,以及被扩展类型的公开成员
,这种方式定义的扩展是可以被全局使用
的。
类内扩展
:如果扩展函数在一个类里面,那么它的访问域是怎么样的呢?看如下代码:
class UploadManager private constructor()
//单例
companion object
private var INSTANCE: UploadManager? = null
fun getInstance(): UploadManager =
INSTANCE ?: synchronized(this)
INSTANCE ?: UploadManager().also INSTANCE = it
//扩展方法
fun String.getStringAfterCut(regular: String, index: Int): String?
split(regular).let list ->
return list.getOrNull(index)
//类的成员方法
fun getFileName(filePath: String): String
return filePath.getStringAfterCut("/", 0) ?: ""
反编译上面的代码,仅仅看扩展函数的部分:
public final class UploadManager
private static UploadManager INSTANCE;
@NotNull
public static final UploadManager.Companion Companion = new UploadManager.Companion((DefaultConstructorMarker)null);
@Nullable
public final String getStringAfterCut(@NotNull String $this$getStringAfterCut, @NotNull String regular, int index)
//...
List var4 = StringsKt.split$default((CharSequence)$this$getStringAfterCut, new String[]regular, false, 0, 6, (Object)null);
//...
return (String)CollectionsKt.getOrNull(var4, index);
//...
可以看到扩展函数变成了UploadManager
类的成员方法,而不是扩展的String
了,因为限制了扩展函数的接受者为String
类型,而方法的调用者只能是UploadManager
的单例对象,所以该扩展函数无法被外部调用,只能在UploadManager
类中被其他成员方法调用。
故类内扩展的访问域仅限于该类当中的所有成员,以及被扩展类型的公开成员,这种方式定义的扩展仅能在该类当中使用。
五、扩展的限制
除了匿名类没有具体的接收类型,其他类都可以被扩展,包括普通类、单例类、密封类、枚举类等等。
扩展的主要用途是取代Java中的各种工具类。
1、Kotlin 扩展不是真正的类成员,因此它无法被它的子类重写。
2、Kotlin 扩展属性无法存储状态。
3、扩展的访问作用域仅限于定义处的成员和接收者类型的公开成员。
六、扩展的实战运用
1、关注点分离
我们看kotlin包中String.kt
的代码:
package kotlin
public class String : Comparable<String>, CharSequence
companion object
public operator fun plus(other: Any?): String
public override val length: Int
public override fun get(index: Int): Char
public override fun subSequence(startIndex: Int, endIndex: Int): CharSequence
public override fun compareTo(other: String): Int
非常简单,只有String.kt
类实现Comparable
和CharSequence
重写的几个方法,那么我们平时使用的trim
、 isEmpty
等工具方法都去哪里了呢? 其实关于String
的工具方法全都通过扩展的形式分离到了Strings.kt
类中,这就是关注点分离,String.kt
类只关注String
自身,而扩展的工具方法全部由Strings.kt
统一管理。
@file:kotlin.jvm.JvmMultifileClass
@file:kotlin.jvm.JvmName("StringsKt")
package kotlin.text
import kotlin.contracts.contract
import kotlin.jvm.JvmName
/**
* Returns a copy of this string converted to upper case using the rules of the default locale.
*/
@Deprecated("Use uppercase() instead.", ReplaceWith("uppercase()"))
@DeprecatedSinceKotlin(warningSince = "1.5")
public expect fun String.toUpperCase(): String
/**
* Returns a copy of this string converted to upper case using Unicode mapping rules of the invariant locale.
*
* This function supports one-to-many and many-to-one character mapping,
* thus the length of the returned string can be different from the length of the original string.
*
* @sample samples.text.Strings.uppercase
*/
@SinceKotlin("1.5")
@WasExperimental(ExperimentalStdlibApi::class)
public expect fun String.uppercase(): String
/**
* Returns a copy of this string converted to lower case using the rules of the default locale.
*/
@Deprecated("Use lowercase() instead.", ReplaceWith("lowercase()"))
@DeprecatedSinceKotlin(warningSince = "1.5")
public expect fun String.toLowerCase(): String
//...略...
2、提升可读性和开发效率
在android开发中经常需要dp转px,如果写成工具类,那么就会这样使用:
//工具类调用
ConvertUtils.dp2px(15f)
但如果写成扩展属性的形式,那么调用就会变成
/**
* 扩展属性:dp转化为px,返回Float值
*/
val Float.dp2px
get() = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
this,
Resources.getSystem().displayMetrics
)
//调用
8F.dp2px
会变得简单直观很多,且全局可调用,大大提高了可读性和开发效率。
个人学习笔记
参考了以下内容
作者:TimeFine
链接:https://juejin.cn/post/7193964032729022520
最后
如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。
如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。
全套视频资料:
一、面试合集
二、源码解析合集
三、开源框架合集
欢迎大家一键三连支持,若需要文中资料,直接点击文末CSDN官方认证微信卡片免费领取↓↓↓
以上是关于Kotlin-扩展的主要内容,如果未能解决你的问题,请参考以下文章
Kotlin 属性:“属性的类型参数必须在其接收者类型中使用”
Kotlin Vocabulary | 使用 Kotlin 中的扩展提升代码可读性