一、扩展函数
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
会变得简略直观许多,且大局可调用,大大提高了可读性和开发功率。
个人学习笔记
参阅了以下内容
扩展:你的能力边界到底在哪里?